customHeaders) {
- this.customHeaders = customHeaders;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiEmbeddingProperties.java
deleted file mode 100644
index a0d4697fa..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiEmbeddingProperties.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure;
-
-import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingOptions;
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.util.Assert;
-
-@ConfigurationProperties(AzureOpenAiEmbeddingProperties.CONFIG_PREFIX)
-public class AzureOpenAiEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.azure.openai.embedding";
-
- /**
- * Enable Azure OpenAI embedding model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private AzureOpenAiEmbeddingOptions options = AzureOpenAiEmbeddingOptions.builder()
- .deploymentName("text-embedding-ada-002")
- .build();
-
- private MetadataMode metadataMode = MetadataMode.EMBED;
-
- public AzureOpenAiEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(AzureOpenAiEmbeddingOptions options) {
- Assert.notNull(options, "Options must not be null");
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- Assert.notNull(metadataMode, "Metadata mode must not be null");
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiImageOptionsProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiImageOptionsProperties.java
deleted file mode 100644
index 51465008b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiImageOptionsProperties.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure;
-
-import org.springframework.ai.azure.openai.AzureOpenAiImageOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for Azure OpenAI image generation options.
- *
- * @author Benoit Moussaud
- * @since 1.0.0 M1
- */
-@ConfigurationProperties(AzureOpenAiImageOptionsProperties.CONFIG_PREFIX)
-public class AzureOpenAiImageOptionsProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.azure.openai.image";
-
- /**
- * Enable Azure OpenAI chat client.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private AzureOpenAiImageOptions options = AzureOpenAiImageOptions.builder().build();
-
- public AzureOpenAiImageOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(AzureOpenAiImageOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfiguration.java
deleted file mode 100644
index f6abd70db..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfiguration.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure;
-
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
-import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
-import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
-import software.amazon.awssdk.regions.Region;
-import software.amazon.awssdk.regions.providers.AwsRegionProvider;
-import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.util.StringUtils;
-
-/**
- * {@link Configuration} for AWS connection.
- *
- * @author Christian Tzolov
- * @author Wei Jiang
- */
-@Configuration
-@EnableConfigurationProperties({ BedrockAwsConnectionProperties.class })
-public class BedrockAwsConnectionConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public AwsCredentialsProvider credentialsProvider(BedrockAwsConnectionProperties properties) {
-
- if (StringUtils.hasText(properties.getAccessKey()) && StringUtils.hasText(properties.getSecretKey())) {
-
- if (StringUtils.hasText(properties.getSessionToken())) {
- return StaticCredentialsProvider.create(AwsSessionCredentials.create(properties.getAccessKey(),
- properties.getSecretKey(), properties.getSessionToken()));
- }
-
- return StaticCredentialsProvider
- .create(AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
- }
-
- return DefaultCredentialsProvider.create();
- }
-
- @Bean
- @ConditionalOnMissingBean
- public AwsRegionProvider regionProvider(BedrockAwsConnectionProperties properties) {
-
- if (StringUtils.hasText(properties.getRegion())) {
- return new StaticRegionProvider(properties.getRegion());
- }
-
- return DefaultAwsRegionProviderChain.builder().build();
- }
-
- static class StaticRegionProvider implements AwsRegionProvider {
-
- private final Region region;
-
- StaticRegionProvider(String region) {
- try {
- this.region = Region.of(region);
- }
- catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("The region '" + region + "' is not a valid region!", e);
- }
- }
-
- @Override
- public Region getRegion() {
- return this.region;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionProperties.java
deleted file mode 100644
index cc47b675b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionProperties.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure;
-
-import java.time.Duration;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for Bedrock AWS connection.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(BedrockAwsConnectionProperties.CONFIG_PREFIX)
-public class BedrockAwsConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.bedrock.aws";
-
- /**
- * AWS region to use. Defaults to us-east-1.
- */
- private String region = "us-east-1";
-
- /**
- * AWS access key.
- */
- private String accessKey;
-
- /**
- * AWS secret key.
- */
- private String secretKey;
-
- /**
- * AWS session token. (optional) When provided the AwsSessionCredentials are used.
- * Otherwise the AwsBasicCredentials are used.
- */
- private String sessionToken;
-
- /**
- * Set model timeout, Defaults 5 min.
- */
- private Duration timeout = Duration.ofMinutes(5L);
-
- public String getRegion() {
- return this.region;
- }
-
- public void setRegion(String awsRegion) {
- this.region = awsRegion;
- }
-
- 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 Duration getTimeout() {
- return this.timeout;
- }
-
- public void setTimeout(Duration timeout) {
- this.timeout = timeout;
- }
-
- public String getSessionToken() {
- return this.sessionToken;
- }
-
- public void setSessionToken(String sessionToken) {
- this.sessionToken = sessionToken;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfiguration.java
deleted file mode 100644
index ce60731c0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfiguration.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.cohere;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-import software.amazon.awssdk.regions.providers.AwsRegionProvider;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionConfiguration;
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
-import org.springframework.ai.bedrock.cohere.BedrockCohereEmbeddingModel;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-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.context.annotation.Import;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for Bedrock Cohere Embedding Model.
- *
- * @author Christian Tzolov
- * @author Wei Jiang
- * @since 0.8.0
- */
-@AutoConfiguration
-@ConditionalOnClass(CohereEmbeddingBedrockApi.class)
-@EnableConfigurationProperties({ BedrockCohereEmbeddingProperties.class, BedrockAwsConnectionProperties.class })
-@ConditionalOnProperty(prefix = BedrockCohereEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true")
-@Import(BedrockAwsConnectionConfiguration.class)
-public class BedrockCohereEmbeddingAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
- public CohereEmbeddingBedrockApi cohereEmbeddingApi(AwsCredentialsProvider credentialsProvider,
- AwsRegionProvider regionProvider, BedrockCohereEmbeddingProperties properties,
- BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
- return new CohereEmbeddingBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
- objectMapper, awsProperties.getTimeout());
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean(CohereEmbeddingBedrockApi.class)
- public BedrockCohereEmbeddingModel cohereEmbeddingModel(CohereEmbeddingBedrockApi cohereEmbeddingApi,
- BedrockCohereEmbeddingProperties properties) {
-
- return new BedrockCohereEmbeddingModel(cohereEmbeddingApi, properties.getOptions());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingProperties.java
deleted file mode 100644
index 131a9832a..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingProperties.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.cohere;
-
-import org.springframework.ai.bedrock.cohere.BedrockCohereEmbeddingOptions;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingModel;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest.InputType;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Bedrock Cohere Embedding autoconfiguration properties.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(BedrockCohereEmbeddingProperties.CONFIG_PREFIX)
-public class BedrockCohereEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.bedrock.cohere.embedding";
-
- /**
- * Enable Bedrock Cohere Embedding Model. False by default.
- */
- private boolean enabled = false;
-
- /**
- * Bedrock Cohere Embedding generative name. Defaults to
- * 'cohere.embed-multilingual-v3'.
- */
- private String model = CohereEmbeddingModel.COHERE_EMBED_MULTILINGUAL_V3.id();
-
- @NestedConfigurationProperty
- private BedrockCohereEmbeddingOptions options = BedrockCohereEmbeddingOptions.builder()
- .inputType(InputType.SEARCH_DOCUMENT)
- .truncate(CohereEmbeddingRequest.Truncate.NONE)
- .build();
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getModel() {
- return this.model;
- }
-
- public void setModel(String model) {
- this.model = model;
- }
-
- public BedrockCohereEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(BedrockCohereEmbeddingOptions options) {
- this.options = options;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfiguration.java
deleted file mode 100644
index 1cfea4c07..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfiguration.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.titan;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-import software.amazon.awssdk.regions.providers.AwsRegionProvider;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionConfiguration;
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
-import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingModel;
-import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-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.context.annotation.Import;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for Bedrock Titan Embedding Model.
- *
- * @author Christian Tzolov
- * @author Wei Jiang
- * @since 0.8.0
- */
-@AutoConfiguration
-@ConditionalOnClass(TitanEmbeddingBedrockApi.class)
-@EnableConfigurationProperties({ BedrockTitanEmbeddingProperties.class, BedrockAwsConnectionProperties.class })
-@ConditionalOnProperty(prefix = BedrockTitanEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true")
-@Import(BedrockAwsConnectionConfiguration.class)
-public class BedrockTitanEmbeddingAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
- public TitanEmbeddingBedrockApi titanEmbeddingBedrockApi(AwsCredentialsProvider credentialsProvider,
- AwsRegionProvider regionProvider, BedrockTitanEmbeddingProperties properties,
- BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
- return new TitanEmbeddingBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
- objectMapper, awsProperties.getTimeout());
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean(TitanEmbeddingBedrockApi.class)
- public BedrockTitanEmbeddingModel titanEmbeddingModel(TitanEmbeddingBedrockApi titanEmbeddingApi,
- BedrockTitanEmbeddingProperties properties) {
-
- return new BedrockTitanEmbeddingModel(titanEmbeddingApi).withInputType(properties.getInputType());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfiguration.java
deleted file mode 100644
index 89d790a9f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfiguration.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.model.chat.client.autoconfigure;
-
-import io.micrometer.observation.ObservationRegistry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.client.ChatClientCustomizer;
-import org.springframework.ai.chat.client.observation.ChatClientInputContentObservationFilter;
-import org.springframework.ai.chat.client.observation.ChatClientObservationConvention;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-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.context.annotation.Scope;
-
-/**
- * {@link EnableAutoConfiguration Auto-configuration} for {@link ChatClient}.
- *
- * This will produce a {@link ChatClient.Builder ChatClient.Builder} bean with the
- * {@code prototype} scope, meaning each injection point will receive a newly cloned
- * instance of the builder.
- *
- * @author Christian Tzolov
- * @author Mark Pollack
- * @author Josh Long
- * @author Arjen Poutsma
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@AutoConfiguration
-@ConditionalOnClass(ChatClient.class)
-@EnableConfigurationProperties(ChatClientBuilderProperties.class)
-@ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
-public class ChatClientAutoConfiguration {
-
- private static final Logger logger = LoggerFactory.getLogger(ChatClientAutoConfiguration.class);
-
- @Bean
- @ConditionalOnMissingBean
- ChatClientBuilderConfigurer chatClientBuilderConfigurer(ObjectProvider customizerProvider) {
- ChatClientBuilderConfigurer configurer = new ChatClientBuilderConfigurer();
- configurer.setChatClientCustomizers(customizerProvider.orderedStream().toList());
- return configurer;
- }
-
- @Bean
- @Scope("prototype")
- @ConditionalOnMissingBean
- ChatClient.Builder chatClientBuilder(ChatClientBuilderConfigurer chatClientBuilderConfigurer, ChatModel chatModel,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- ChatClient.Builder builder = ChatClient.builder(chatModel,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
- observationConvention.getIfUnique(() -> null));
- return chatClientBuilderConfigurer.configure(builder);
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatClientBuilderProperties.CONFIG_PREFIX + ".observations", name = "include-input",
- havingValue = "true")
- ChatClientInputContentObservationFilter chatClientInputContentObservationFilter() {
- logger.warn(
- "You have enabled the inclusion of the input content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
- return new ChatClientInputContentObservationFilter();
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderConfigurer.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderConfigurer.java
deleted file mode 100644
index f0448058c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderConfigurer.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.model.chat.client.autoconfigure;
-
-import java.util.List;
-
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.client.ChatClientCustomizer;
-
-/**
- * Builder for configuring a {@link ChatClient.Builder}.
- *
- * @author Christian Tzolov
- * @author Mark Pollack
- * @author Josh Long
- * @author Arjen Poutsma
- * @since 1.0.0 M1
- */
-public class ChatClientBuilderConfigurer {
-
- private List customizers;
-
- void setChatClientCustomizers(List customizers) {
- this.customizers = customizers;
- }
-
- /**
- * Configure the specified {@link ChatClient.Builder}. The builder can be further
- * tuned and default settings can be overridden.
- * @param builder the {@link ChatClient.Builder} instance to configure
- * @return the configured builder
- */
- public ChatClient.Builder configure(ChatClient.Builder builder) {
- applyCustomizers(builder);
- return builder;
- }
-
- private void applyCustomizers(ChatClient.Builder builder) {
- if (this.customizers != null) {
- for (ChatClientCustomizer customizer : this.customizers) {
- customizer.customize(builder);
- }
- }
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderProperties.java
deleted file mode 100644
index 0ca9023d7..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientBuilderProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.chat.client.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for the chat client builder.
- *
- * @author Christian Tzolov
- * @author Mark Pollack
- * @author Josh Long
- * @author Arjen Poutsma
- * @since 1.0.0
- */
-@ConfigurationProperties(ChatClientBuilderProperties.CONFIG_PREFIX)
-public class ChatClientBuilderProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.chat.client";
-
- /**
- * Enable chat client builder.
- */
- private boolean enabled = true;
-
- private Observations observations = new Observations();
-
- public Observations getObservations() {
- return this.observations;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public static class Observations {
-
- /**
- * Whether to include the input content in the observations.
- */
- private boolean includeInput = false;
-
- public boolean isIncludeInput() {
- return this.includeInput;
- }
-
- public void setIncludeInput(boolean includeCompletion) {
- this.includeInput = includeCompletion;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/CommonChatMemoryProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/CommonChatMemoryProperties.java
deleted file mode 100644
index 00ea424c4..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/CommonChatMemoryProperties.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.model.chat.memory.autoconfigure;
-
-/**
- * Configuration properties for the common chat memory.
- *
- * @author Mick Semb Wever
- * @since 1.0.0
- */
-public class CommonChatMemoryProperties {
-
- private boolean initializeSchema = true;
-
- public boolean isInitializeSchema() {
- return this.initializeSchema;
- }
-
- public void setInitializeSchema(boolean initializeSchema) {
- this.initializeSchema = initializeSchema;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfiguration.java
deleted file mode 100644
index 937a49beb..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfiguration.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.chat.memory.autoconfigure.cassandra;
-
-import com.datastax.oss.driver.api.core.CqlSession;
-
-import org.springframework.ai.chat.memory.cassandra.CassandraChatMemory;
-import org.springframework.ai.chat.memory.cassandra.CassandraChatMemoryConfig;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for {@link CassandraChatMemory}.
- *
- * @author Mick Semb Wever
- * @author Jihoon Kim
- * @since 1.0.0
- */
-@AutoConfiguration(after = CassandraAutoConfiguration.class)
-@ConditionalOnClass({ CassandraChatMemory.class, CqlSession.class })
-@EnableConfigurationProperties(CassandraChatMemoryProperties.class)
-public class CassandraChatMemoryAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public CassandraChatMemory chatMemory(CassandraChatMemoryProperties properties, CqlSession cqlSession) {
-
- var builder = CassandraChatMemoryConfig.builder().withCqlSession(cqlSession);
-
- builder = builder.withKeyspaceName(properties.getKeyspace())
- .withTableName(properties.getTable())
- .withAssistantColumnName(properties.getAssistantColumn())
- .withUserColumnName(properties.getUserColumn());
-
- if (!properties.isInitializeSchema()) {
- builder = builder.disallowSchemaChanges();
- }
- if (null != properties.getTimeToLive()) {
- builder = builder.withTimeToLive(properties.getTimeToLive());
- }
-
- return CassandraChatMemory.create(builder.build());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryProperties.java
deleted file mode 100644
index 14ae105ab..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryProperties.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.model.chat.memory.autoconfigure.cassandra;
-
-import java.time.Duration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.chat.memory.autoconfigure.CommonChatMemoryProperties;
-import org.springframework.ai.chat.memory.cassandra.CassandraChatMemoryConfig;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.lang.Nullable;
-
-/**
- * Configuration properties for Cassandra chat memory.
- *
- * @author Mick Semb Wever
- * @author Jihoon Kim
- * @since 1.0.0
- */
-@ConfigurationProperties(CassandraChatMemoryProperties.CONFIG_PREFIX)
-public class CassandraChatMemoryProperties extends CommonChatMemoryProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.chat.memory.cassandra";
-
- private static final Logger logger = LoggerFactory.getLogger(CassandraChatMemoryProperties.class);
-
- private String keyspace = CassandraChatMemoryConfig.DEFAULT_KEYSPACE_NAME;
-
- private String table = CassandraChatMemoryConfig.DEFAULT_TABLE_NAME;
-
- private String assistantColumn = CassandraChatMemoryConfig.DEFAULT_ASSISTANT_COLUMN_NAME;
-
- private String userColumn = CassandraChatMemoryConfig.DEFAULT_USER_COLUMN_NAME;
-
- private Duration timeToLive = null;
-
- public String getKeyspace() {
- return this.keyspace;
- }
-
- public void setKeyspace(String keyspace) {
- this.keyspace = keyspace;
- }
-
- public String getTable() {
- return this.table;
- }
-
- public void setTable(String table) {
- this.table = table;
- }
-
- public String getAssistantColumn() {
- return this.assistantColumn;
- }
-
- public void setAssistantColumn(String assistantColumn) {
- this.assistantColumn = assistantColumn;
- }
-
- public String getUserColumn() {
- return this.userColumn;
- }
-
- public void setUserColumn(String userColumn) {
- this.userColumn = userColumn;
- }
-
- @Nullable
- public Duration getTimeToLive() {
- return this.timeToLive;
- }
-
- public void setTimeToLive(Duration timeToLive) {
- this.timeToLive = timeToLive;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfiguration.java
deleted file mode 100644
index 81aee1992..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfiguration.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.model.chat.observation.autoconfigure;
-
-import java.util.List;
-
-import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.tracing.Tracer;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.chat.client.advisor.observation.AdvisorObservationContext;
-import org.springframework.ai.chat.client.observation.ChatClientObservationContext;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelMeterObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelObservationContext;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationHandler;
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationContext;
-import org.springframework.ai.image.observation.ImageModelObservationContext;
-import org.springframework.ai.model.observation.ErrorLoggingObservationHandler;
-import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-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.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Auto-configuration for Spring AI chat model observations.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@AutoConfiguration(
- afterName = { "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration" })
-@ConditionalOnClass(ChatModel.class)
-@EnableConfigurationProperties({ ChatObservationProperties.class })
-public class ChatObservationAutoConfiguration {
-
- private static final Logger logger = LoggerFactory.getLogger(ChatObservationAutoConfiguration.class);
-
- private static void logPromptContentWarning() {
- logger.warn(
- "You have enabled the inclusion of the prompt content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
- }
-
- private static void logCompletionWarning() {
- logger.warn(
- "You have enabled the inclusion of the completion content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean(MeterRegistry.class)
- ChatModelMeterObservationHandler chatModelMeterObservationHandler(ObjectProvider meterRegistry) {
- return new ChatModelMeterObservationHandler(meterRegistry.getObject());
- }
-
- /**
- * The chat content is typically too big to be included in an observation as span
- * attributes. That's why the preferred way to store it is as span events, which are
- * supported by OpenTelemetry but not yet surfaced through the Micrometer APIs. This
- * primary/fallback configuration is a temporary solution until
- * https://github.com/micrometer-metrics/micrometer/issues/5238 is delivered.
- */
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass(OtelTracer.class)
- @ConditionalOnBean(OtelTracer.class)
- static class PrimaryChatContentObservationConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-prompt",
- havingValue = "true")
- ChatModelPromptContentObservationHandler chatModelPromptContentObservationHandler() {
- logPromptContentWarning();
- return new ChatModelPromptContentObservationHandler();
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-completion",
- havingValue = "true")
- ChatModelCompletionObservationHandler chatModelCompletionObservationHandler() {
- logCompletionWarning();
- return new ChatModelCompletionObservationHandler();
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnMissingClass("io.micrometer.tracing.otel.bridge.OtelTracer")
- static class FallbackChatContentObservationConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-prompt",
- havingValue = "true")
- ChatModelPromptContentObservationFilter chatModelPromptObservationFilter() {
- logPromptContentWarning();
- return new ChatModelPromptContentObservationFilter();
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-completion",
- havingValue = "true")
- ChatModelCompletionObservationFilter chatModelCompletionObservationFilter() {
- logCompletionWarning();
- return new ChatModelCompletionObservationFilter();
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass(Tracer.class)
- @ConditionalOnBean(Tracer.class)
- static class TracingChatContentObservationConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ChatObservationProperties.CONFIG_PREFIX, name = "include-error-logging",
- havingValue = "true")
- public ErrorLoggingObservationHandler errorLoggingObservationHandler(Tracer tracer) {
- return new ErrorLoggingObservationHandler(tracer,
- List.of(EmbeddingModelObservationContext.class, ImageModelObservationContext.class,
- ChatModelObservationContext.class, ChatClientObservationContext.class,
- AdvisorObservationContext.class, VectorStoreObservationContext.class));
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationProperties.java
deleted file mode 100644
index be37d1b26..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.chat.observation.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for chat model observations.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@ConfigurationProperties(ChatObservationProperties.CONFIG_PREFIX)
-public class ChatObservationProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.chat.observations";
-
- /**
- * Whether to include the completion content in the observations.
- */
- private boolean includeCompletion = false;
-
- /**
- * Whether to include the prompt content in the observations.
- */
- private boolean includePrompt = false;
-
- /**
- * Whether to include error logging in the observations.
- */
- private boolean includeErrorLogging = false;
-
- public boolean isIncludeCompletion() {
- return this.includeCompletion;
- }
-
- public void setIncludeCompletion(boolean includeCompletion) {
- this.includeCompletion = includeCompletion;
- }
-
- public boolean isIncludePrompt() {
- return this.includePrompt;
- }
-
- public void setIncludePrompt(boolean includePrompt) {
- this.includePrompt = includePrompt;
- }
-
- public boolean isIncludeErrorLogging() {
- return this.includeErrorLogging;
- }
-
- public void setIncludeErrorLogging(boolean includeErrorLogging) {
- this.includeErrorLogging = includeErrorLogging;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/package-info.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/package-info.java
deleted file mode 100644
index 66bef736b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/chat/observation/autoconfigure/package-info.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * Auto-configuration for chat observation.
- */
-@NonNullApi
-@NonNullFields
-package org.springframework.ai.model.chat.observation.autoconfigure;
-
-import org.springframework.lang.NonNullApi;
-import org.springframework.lang.NonNullFields;
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfiguration.java
deleted file mode 100644
index ee0902343..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfiguration.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.model.embedding.observation.autoconfigure;
-
-import io.micrometer.core.instrument.MeterRegistry;
-
-import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.embedding.observation.EmbeddingModelMeterObservationHandler;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-
-/**
- * Auto-configuration for Spring AI embedding model observations.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@AutoConfiguration(
- afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration")
-@ConditionalOnClass(EmbeddingModel.class)
-public class EmbeddingObservationAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnBean(MeterRegistry.class)
- EmbeddingModelMeterObservationHandler embeddingModelMeterObservationHandler(
- ObjectProvider meterRegistry) {
- return new EmbeddingModelMeterObservationHandler(meterRegistry.getObject());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/package-info.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/package-info.java
deleted file mode 100644
index 654b59b6f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/embedding/observation/autoconfigure/package-info.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * Auto-configuration for embedding observation.
- */
-@NonNullApi
-@NonNullFields
-package org.springframework.ai.model.embedding.observation.autoconfigure;
-
-import org.springframework.lang.NonNullApi;
-import org.springframework.lang.NonNullFields;
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfiguration.java
deleted file mode 100644
index a79a6517d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfiguration.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.model.huggingface.autoconfigure;
-
-import org.springframework.ai.huggingface.HuggingfaceChatModel;
-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.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-
-@AutoConfiguration
-@ConditionalOnClass(HuggingfaceChatModel.class)
-@EnableConfigurationProperties(HuggingfaceChatProperties.class)
-public class HuggingfaceChatAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = HuggingfaceChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public HuggingfaceChatModel huggingfaceChatModel(HuggingfaceChatProperties huggingfaceChatProperties) {
- return new HuggingfaceChatModel(huggingfaceChatProperties.getApiKey(), huggingfaceChatProperties.getUrl());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatProperties.java
deleted file mode 100644
index 371df322f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatProperties.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.model.huggingface.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for Hugging Face chat model.
- *
- * @author Christian Tzolov
- * @author Josh Long
- * @author Mark Pollack
- * @author Thomas Vitale
- */
-@ConfigurationProperties(HuggingfaceChatProperties.CONFIG_PREFIX)
-public class HuggingfaceChatProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.huggingface.chat";
-
- /**
- * API Key to authenticate with the Inference Endpoint.
- */
- private String apiKey;
-
- /**
- * URL of the Inference Endpoint.
- */
- private String url;
-
- /**
- * Enable Hugging Face chat model.
- */
- private boolean enabled = true;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getUrl() {
- return this.url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfiguration.java
deleted file mode 100644
index f97e12f03..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfiguration.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.model.image.observation.autoconfigure;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.image.ImageModel;
-import org.springframework.ai.image.observation.ImageModelPromptContentObservationFilter;
-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.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-
-/**
- * Auto-configuration for Spring AI image model observations.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@AutoConfiguration(
- afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration.class")
-@ConditionalOnClass(ImageModel.class)
-@EnableConfigurationProperties({ ImageObservationProperties.class })
-public class ImageObservationAutoConfiguration {
-
- private static final Logger logger = LoggerFactory.getLogger(ImageObservationAutoConfiguration.class);
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ImageObservationProperties.CONFIG_PREFIX, name = "include-prompt",
- havingValue = "true")
- ImageModelPromptContentObservationFilter imageModelPromptObservationFilter() {
- logger.warn(
- "You have enabled the inclusion of the image prompt content in the observations, with the risk of exposing sensitive or private information. Please, be careful!");
- return new ImageModelPromptContentObservationFilter();
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationProperties.java
deleted file mode 100644
index 7d372d986..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationProperties.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.model.image.observation.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for image model observations.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@ConfigurationProperties(ImageObservationProperties.CONFIG_PREFIX)
-public class ImageObservationProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.image.observations";
-
- /**
- * Whether to include the prompt content in the observations.
- */
- private boolean includePrompt = false;
-
- public boolean isIncludePrompt() {
- return this.includePrompt;
- }
-
- public void setIncludePrompt(boolean includePrompt) {
- this.includePrompt = includePrompt;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/package-info.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/package-info.java
deleted file mode 100644
index 983cc5a16..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/image/observation/autoconfigure/package-info.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * Auto-configuration for image observation.
- */
-@NonNullApi
-@NonNullFields
-package org.springframework.ai.model.image.observation.autoconfigure;
-
-import org.springframework.lang.NonNullApi;
-import org.springframework.lang.NonNullFields;
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatProperties.java
deleted file mode 100644
index a04b1655c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxChatProperties.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import org.springframework.ai.minimax.MiniMaxChatOptions;
-import org.springframework.ai.minimax.api.MiniMaxApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for MiniMax chat model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(MiniMaxChatProperties.CONFIG_PREFIX)
-public class MiniMaxChatProperties extends MiniMaxParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.minimax.chat";
-
- public static final String DEFAULT_CHAT_MODEL = MiniMaxApi.ChatModel.ABAB_5_5_Chat.value;
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- /**
- * Enable MiniMax chat model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private MiniMaxChatOptions options = MiniMaxChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public MiniMaxChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(MiniMaxChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxConnectionProperties.java
deleted file mode 100644
index e41162a39..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxConnectionProperties.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(MiniMaxConnectionProperties.CONFIG_PREFIX)
-public class MiniMaxConnectionProperties extends MiniMaxParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.minimax";
-
- public static final String DEFAULT_BASE_URL = "https://api.minimax.chat";
-
- public MiniMaxConnectionProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingProperties.java
deleted file mode 100644
index 465a573f9..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxEmbeddingProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
-import org.springframework.ai.minimax.api.MiniMaxApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for MiniMax embedding model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(MiniMaxEmbeddingProperties.CONFIG_PREFIX)
-public class MiniMaxEmbeddingProperties extends MiniMaxParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.minimax.embedding";
-
- public static final String DEFAULT_EMBEDDING_MODEL = MiniMaxApi.EmbeddingModel.Embo_01.value;
-
- /**
- * Enable MiniMax embedding model.
- */
- private boolean enabled = true;
-
- private MetadataMode metadataMode = MetadataMode.EMBED;
-
- @NestedConfigurationProperty
- private MiniMaxEmbeddingOptions options = MiniMaxEmbeddingOptions.builder().model(DEFAULT_EMBEDDING_MODEL).build();
-
- public MiniMaxEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(MiniMaxEmbeddingOptions options) {
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxParentProperties.java
deleted file mode 100644
index 2927c6ea3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxParentProperties.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-/**
- * @author Geng Rong
- */
-class MiniMaxParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatProperties.java
deleted file mode 100644
index 19a782a8e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiChatProperties.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-import org.springframework.ai.mistralai.MistralAiChatOptions;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for Mistral AI chat.
- *
- * @author Ricken Bazolo
- * @author Christian Tzolov
- * @author Thomas Vitale
- * @author Alexandros Pappas
- * @since 0.8.1
- */
-@ConfigurationProperties(MistralAiChatProperties.CONFIG_PREFIX)
-public class MistralAiChatProperties extends MistralAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.mistralai.chat";
-
- public static final String DEFAULT_CHAT_MODEL = MistralAiApi.ChatModel.SMALL.getValue();
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- private static final Double DEFAULT_TOP_P = 1.0;
-
- private static final Boolean IS_ENABLED = false;
-
- /**
- * Enable OpenAI chat model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private MistralAiChatOptions options = MistralAiChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .safePrompt(!IS_ENABLED)
- .topP(DEFAULT_TOP_P)
- .build();
-
- public MistralAiChatProperties() {
- super.setBaseUrl(MistralAiCommonProperties.DEFAULT_BASE_URL);
- }
-
- public MistralAiChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(MistralAiChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiCommonProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiCommonProperties.java
deleted file mode 100644
index 3d1a90bfc..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiCommonProperties.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Common properties for Mistral AI.
- *
- * @author Ricken Bazolo
- * @author Christian Tzolov
- * @since 0.8.1
- */
-@ConfigurationProperties(MistralAiCommonProperties.CONFIG_PREFIX)
-public class MistralAiCommonProperties extends MistralAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.mistralai";
-
- public static final String DEFAULT_BASE_URL = "https://api.mistral.ai";
-
- public MistralAiCommonProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingProperties.java
deleted file mode 100644
index f8c3498be..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiEmbeddingProperties.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.mistralai.MistralAiEmbeddingOptions;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for MistralAI embedding model.
- *
- * @author Ricken Bazolo
- * @since 0.8.1
- */
-@ConfigurationProperties(MistralAiEmbeddingProperties.CONFIG_PREFIX)
-public class MistralAiEmbeddingProperties extends MistralAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.mistralai.embedding";
-
- public static final String DEFAULT_EMBEDDING_MODEL = MistralAiApi.EmbeddingModel.EMBED.getValue();
-
- public static final String DEFAULT_ENCODING_FORMAT = "float";
-
- public MetadataMode metadataMode = MetadataMode.EMBED;
-
- /**
- * Enable MistralAI embedding model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private MistralAiEmbeddingOptions options = MistralAiEmbeddingOptions.builder()
- .withModel(DEFAULT_EMBEDDING_MODEL)
- .withEncodingFormat(DEFAULT_ENCODING_FORMAT)
- .build();
-
- public MistralAiEmbeddingProperties() {
- super.setBaseUrl(MistralAiCommonProperties.DEFAULT_BASE_URL);
- }
-
- public MistralAiEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(MistralAiEmbeddingOptions options) {
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiParentProperties.java
deleted file mode 100644
index 0da844cc2..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiParentProperties.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-/**
- * Parent properties for Mistral AI.
- *
- * @author Ricken Bazolo
- * @since 0.8.1
- */
-public class MistralAiParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotChatProperties.java
deleted file mode 100644
index 3933eba3e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotChatProperties.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure;
-
-import org.springframework.ai.moonshot.MoonshotChatOptions;
-import org.springframework.ai.moonshot.api.MoonshotApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for Moonshot chat client.
- *
- * @author Geng Rong
- * @author Alexandros Pappas
- */
-@ConfigurationProperties(MoonshotChatProperties.CONFIG_PREFIX)
-public class MoonshotChatProperties extends MoonshotParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.moonshot.chat";
-
- public static final String DEFAULT_CHAT_MODEL = MoonshotApi.ChatModel.MOONSHOT_V1_8K.getValue();
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- /**
- * Enable Moonshot chat client.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private MoonshotChatOptions options = MoonshotChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public MoonshotChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(MoonshotChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotCommonProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotCommonProperties.java
deleted file mode 100644
index 7d9461f91..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotCommonProperties.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Parent properties for Moonshot.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(MoonshotCommonProperties.CONFIG_PREFIX)
-public class MoonshotCommonProperties extends MoonshotParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.moonshot";
-
- public static final String DEFAULT_BASE_URL = "https://api.moonshot.cn";
-
- public MoonshotCommonProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotParentProperties.java
deleted file mode 100644
index 23a07ced3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotParentProperties.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure;
-
-/**
- * Parent properties for Moonshot.
- *
- * @author Geng Rong
- */
-public class MoonshotParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCICohereChatModelProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCICohereChatModelProperties.java
deleted file mode 100644
index ebed22636..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCICohereChatModelProperties.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-import org.springframework.ai.oci.cohere.OCICohereChatOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for OCI Cohere chat model.
- *
- * @author Anders Swanson
- */
-@ConfigurationProperties(OCICohereChatModelProperties.CONFIG_PREFIX)
-public class OCICohereChatModelProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.oci.genai.cohere.chat";
-
- private static final String DEFAULT_SERVING_MODE = ServingMode.ON_DEMAND.getMode();
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- private boolean enabled;
-
- @NestedConfigurationProperty
- private OCICohereChatOptions options = OCICohereChatOptions.builder()
- .servingMode(DEFAULT_SERVING_MODE)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public OCICohereChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OCICohereChatOptions options) {
- this.options = options;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIConnectionProperties.java
deleted file mode 100644
index 69f33d910..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIConnectionProperties.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-import java.nio.file.Paths;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.util.StringUtils;
-
-/**
- * Configuration properties for OCI connection.
- *
- * @author Anders Swanson
- */
-@ConfigurationProperties(OCIConnectionProperties.CONFIG_PREFIX)
-public class OCIConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.oci.genai";
-
- private static final String DEFAULT_PROFILE = "DEFAULT";
-
- private AuthenticationType authenticationType = AuthenticationType.FILE;
-
- private String profile;
-
- private String file = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
-
- private String tenantId;
-
- private String userId;
-
- private String fingerprint;
-
- private String privateKey;
-
- private String passPhrase;
-
- private String region = "us-chicago-1";
-
- private String endpoint;
-
- public String getRegion() {
- return this.region;
- }
-
- public void setRegion(String region) {
- this.region = region;
- }
-
- public String getPassPhrase() {
- return this.passPhrase;
- }
-
- public void setPassPhrase(String passPhrase) {
- this.passPhrase = passPhrase;
- }
-
- public String getPrivateKey() {
- return this.privateKey;
- }
-
- public void setPrivateKey(String privateKey) {
- this.privateKey = privateKey;
- }
-
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- public void setFingerprint(String fingerprint) {
- this.fingerprint = fingerprint;
- }
-
- public String getUserId() {
- return this.userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public String getTenantId() {
- return this.tenantId;
- }
-
- public void setTenantId(String tenantId) {
- this.tenantId = tenantId;
- }
-
- public String getFile() {
- return this.file;
- }
-
- public void setFile(String file) {
- this.file = file;
- }
-
- public String getProfile() {
- return StringUtils.hasText(this.profile) ? this.profile : DEFAULT_PROFILE;
- }
-
- public void setProfile(String profile) {
- this.profile = profile;
- }
-
- public AuthenticationType getAuthenticationType() {
- return this.authenticationType;
- }
-
- public void setAuthenticationType(AuthenticationType authenticationType) {
- this.authenticationType = authenticationType;
- }
-
- public String getEndpoint() {
- return this.endpoint;
- }
-
- public void setEndpoint(String endpoint) {
- this.endpoint = endpoint;
- }
-
- public enum AuthenticationType {
-
- FILE("file"), INSTANCE_PRINCIPAL("instance-principal"), WORKLOAD_IDENTITY("workload-identity"),
- SIMPLE("simple");
-
- private final String authType;
-
- AuthenticationType(String authType) {
- this.authType = authType;
- }
-
- public String getAuthType() {
- return this.authType;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIEmbeddingModelProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIEmbeddingModelProperties.java
deleted file mode 100644
index 3ba1ea993..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIEmbeddingModelProperties.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-import com.oracle.bmc.generativeaiinference.model.EmbedTextDetails;
-
-import org.springframework.ai.oci.OCIEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for OCI embedding model.
- *
- * @author Anders Swanson
- */
-@ConfigurationProperties(OCIEmbeddingModelProperties.CONFIG_PREFIX)
-public class OCIEmbeddingModelProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.oci.genai.embedding";
-
- private ServingMode servingMode = ServingMode.ON_DEMAND;
-
- private EmbedTextDetails.Truncate truncate = EmbedTextDetails.Truncate.End;
-
- private String compartment;
-
- private String model;
-
- private boolean enabled;
-
- public OCIEmbeddingOptions getEmbeddingOptions() {
- return OCIEmbeddingOptions.builder()
- .compartment(this.compartment)
- .model(this.model)
- .servingMode(this.servingMode.getMode())
- .truncate(this.truncate)
- .build();
- }
-
- public ServingMode getServingMode() {
- return this.servingMode;
- }
-
- public void setServingMode(ServingMode servingMode) {
- this.servingMode = servingMode;
- }
-
- public String getCompartment() {
- return this.compartment;
- }
-
- public void setCompartment(String compartment) {
- this.compartment = compartment;
- }
-
- public String getModel() {
- return this.model;
- }
-
- public void setModel(String model) {
- this.model = model;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public EmbedTextDetails.Truncate getTruncate() {
- return this.truncate;
- }
-
- public void setTruncate(EmbedTextDetails.Truncate truncate) {
- this.truncate = truncate;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/ServingMode.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/ServingMode.java
deleted file mode 100644
index 49b09ae13..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/oci/genai/autoconfigure/ServingMode.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-/**
- * OCI serving mode.
- *
- * @author Anders Swanson
- */
-public enum ServingMode {
-
- ON_DEMAND("on-demand"), DEDICATED("dedicated");
-
- private final String mode;
-
- ServingMode(String mode) {
- this.mode = mode;
- }
-
- public String getMode() {
- return this.mode;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaAutoConfiguration.java
deleted file mode 100644
index 7d7948f8e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaAutoConfiguration.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.ollama.autoconfigure;
-
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
-import org.springframework.ai.chat.observation.ChatModelObservationConvention;
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
-import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
-import org.springframework.ai.model.function.FunctionCallbackResolver;
-import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.OllamaEmbeddingModel;
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.management.ModelManagementOptions;
-import org.springframework.ai.ollama.management.PullModelStrategy;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.web.client.RestClient;
-import org.springframework.web.reactive.function.client.WebClient;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for Ollama Chat Client.
- *
- * @author Christian Tzolov
- * @author Eddú Meléndez
- * @author Thomas Vitale
- * @since 0.8.0
- */
-@AutoConfiguration(after = { RestClientAutoConfiguration.class, ToolCallingAutoConfiguration.class })
-@ConditionalOnClass(OllamaApi.class)
-@EnableConfigurationProperties({ OllamaChatProperties.class, OllamaEmbeddingProperties.class,
- OllamaConnectionProperties.class, OllamaInitializationProperties.class })
-@ImportAutoConfiguration(classes = { RestClientAutoConfiguration.class, ToolCallingAutoConfiguration.class,
- WebClientAutoConfiguration.class })
-public class OllamaAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean(OllamaConnectionDetails.class)
- public PropertiesOllamaConnectionDetails ollamaConnectionDetails(OllamaConnectionProperties properties) {
- return new PropertiesOllamaConnectionDetails(properties);
- }
-
- @Bean
- @ConditionalOnMissingBean
- public OllamaApi ollamaApi(OllamaConnectionDetails connectionDetails,
- ObjectProvider restClientBuilderProvider,
- ObjectProvider webClientBuilderProvider) {
- return new OllamaApi(connectionDetails.getBaseUrl(),
- restClientBuilderProvider.getIfAvailable(RestClient::builder),
- webClientBuilderProvider.getIfAvailable(WebClient::builder));
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OllamaChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OllamaChatModel ollamaChatModel(OllamaApi ollamaApi, OllamaChatProperties properties,
- OllamaInitializationProperties initProperties, ToolCallingManager toolCallingManager,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
- var chatModelPullStrategy = initProperties.getChat().isInclude() ? initProperties.getPullModelStrategy()
- : PullModelStrategy.NEVER;
-
- var chatModel = OllamaChatModel.builder()
- .ollamaApi(ollamaApi)
- .defaultOptions(properties.getOptions())
- .toolCallingManager(toolCallingManager)
- .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
- .modelManagementOptions(
- new ModelManagementOptions(chatModelPullStrategy, initProperties.getChat().getAdditionalModels(),
- initProperties.getTimeout(), initProperties.getMaxRetries()))
- .build();
-
- observationConvention.ifAvailable(chatModel::setObservationConvention);
-
- return chatModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OllamaEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OllamaEmbeddingModel ollamaEmbeddingModel(OllamaApi ollamaApi, OllamaEmbeddingProperties properties,
- OllamaInitializationProperties initProperties, ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
- var embeddingModelPullStrategy = initProperties.getEmbedding().isInclude()
- ? initProperties.getPullModelStrategy() : PullModelStrategy.NEVER;
-
- var embeddingModel = OllamaEmbeddingModel.builder()
- .ollamaApi(ollamaApi)
- .defaultOptions(properties.getOptions())
- .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
- .modelManagementOptions(new ModelManagementOptions(embeddingModelPullStrategy,
- initProperties.getEmbedding().getAdditionalModels(), initProperties.getTimeout(),
- initProperties.getMaxRetries()))
- .build();
-
- observationConvention.ifAvailable(embeddingModel::setObservationConvention);
-
- return embeddingModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
- DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
- manager.setApplicationContext(context);
- return manager;
- }
-
- static class PropertiesOllamaConnectionDetails implements OllamaConnectionDetails {
-
- private final OllamaConnectionProperties properties;
-
- PropertiesOllamaConnectionDetails(OllamaConnectionProperties properties) {
- this.properties = properties;
- }
-
- @Override
- public String getBaseUrl() {
- return this.properties.getBaseUrl();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatProperties.java
deleted file mode 100644
index 22569554a..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatProperties.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.springframework.ai.ollama.api.OllamaModel;
-import org.springframework.ai.ollama.api.OllamaOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Ollama Chat autoconfiguration properties.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(OllamaChatProperties.CONFIG_PREFIX)
-public class OllamaChatProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.ollama.chat";
-
- /**
- * Enable Ollama chat model.
- */
- private boolean enabled = true;
-
- /**
- * Client lever Ollama options. Use this property to configure generative temperature,
- * topK and topP and alike parameters. The null values are ignored defaulting to the
- * generative's defaults.
- */
- @NestedConfigurationProperty
- private OllamaOptions options = OllamaOptions.builder().model(OllamaModel.MISTRAL.id()).build();
-
- public String getModel() {
- return this.options.getModel();
- }
-
- public void setModel(String model) {
- this.options.setModel(model);
- }
-
- public OllamaOptions getOptions() {
- return this.options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionDetails.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionDetails.java
deleted file mode 100644
index eef0f2af3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionDetails.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
-
-/**
- * Connection details for an Ollama service.
- *
- * @author Eddú Meléndez
- */
-public interface OllamaConnectionDetails extends ConnectionDetails {
-
- String getBaseUrl();
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionProperties.java
deleted file mode 100644
index 58e7e1c62..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaConnectionProperties.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Ollama connection autoconfiguration properties.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(OllamaConnectionProperties.CONFIG_PREFIX)
-public class OllamaConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.ollama";
-
- /**
- * Base URL where Ollama API server is running.
- */
- private String baseUrl = "http://localhost:11434";
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingProperties.java
deleted file mode 100644
index 287c52999..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingProperties.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.springframework.ai.ollama.api.OllamaModel;
-import org.springframework.ai.ollama.api.OllamaOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Ollama Embedding autoconfiguration properties.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(OllamaEmbeddingProperties.CONFIG_PREFIX)
-public class OllamaEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.ollama.embedding";
-
- /**
- * Enable Ollama embedding model.
- */
- private boolean enabled = true;
-
- /**
- * Client lever Ollama options. Use this property to configure generative temperature,
- * topK and topP and alike parameters. The null values are ignored defaulting to the
- * generative's defaults.
- */
- @NestedConfigurationProperty
- private OllamaOptions options = OllamaOptions.builder().model(OllamaModel.MXBAI_EMBED_LARGE.id()).build();
-
- public String getModel() {
- return this.options.getModel();
- }
-
- public void setModel(String model) {
- this.options.setModel(model);
- }
-
- public OllamaOptions getOptions() {
- return this.options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaInitializationProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaInitializationProperties.java
deleted file mode 100644
index 31f92fc3d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaInitializationProperties.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import java.time.Duration;
-import java.util.List;
-
-import org.springframework.ai.ollama.management.PullModelStrategy;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Ollama initialization configuration properties.
- *
- * @author Thomas Vitale
- * @since 1.0.0
- */
-@ConfigurationProperties(OllamaInitializationProperties.CONFIG_PREFIX)
-public class OllamaInitializationProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.ollama.init";
-
- /**
- * Chat models initialization settings.
- */
- private final ModelTypeInit chat = new ModelTypeInit();
-
- /**
- * Embedding models initialization settings.
- */
- private final ModelTypeInit embedding = new ModelTypeInit();
-
- /**
- * Whether to pull models at startup-time and how.
- */
- private PullModelStrategy pullModelStrategy = PullModelStrategy.NEVER;
-
- /**
- * How long to wait for a model to be pulled.
- */
- private Duration timeout = Duration.ofMinutes(5);
-
- /**
- * Maximum number of retries for the model pull operation.
- */
- private int maxRetries = 0;
-
- public PullModelStrategy getPullModelStrategy() {
- return this.pullModelStrategy;
- }
-
- public void setPullModelStrategy(PullModelStrategy pullModelStrategy) {
- this.pullModelStrategy = pullModelStrategy;
- }
-
- public ModelTypeInit getChat() {
- return this.chat;
- }
-
- public ModelTypeInit getEmbedding() {
- return this.embedding;
- }
-
- public Duration getTimeout() {
- return this.timeout;
- }
-
- public void setTimeout(Duration timeout) {
- this.timeout = timeout;
- }
-
- public int getMaxRetries() {
- return this.maxRetries;
- }
-
- public void setMaxRetries(int maxRetries) {
- this.maxRetries = maxRetries;
- }
-
- public static class ModelTypeInit {
-
- /**
- * Include this type of models in the initialization task.
- */
- private boolean include = true;
-
- /**
- * Additional models to initialize besides the ones configured via default
- * properties.
- */
- private List additionalModels = List.of();
-
- public boolean isInclude() {
- return this.include;
- }
-
- public void setInclude(boolean include) {
- this.include = include;
- }
-
- public List getAdditionalModels() {
- return this.additionalModels;
- }
-
- public void setAdditionalModels(List additionalModels) {
- this.additionalModels = additionalModels;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechProperties.java
deleted file mode 100644
index 8508e5d8e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioSpeechProperties.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.openai.OpenAiAudioSpeechOptions;
-import org.springframework.ai.openai.api.OpenAiAudioApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for OpenAI audio speech.
- *
- * Default values for required options are model = tts_1, response format = mp3, voice =
- * alloy, and speed = 1.
- *
- * @author Ahmed Yousri
- * @author Stefan Vassilev
- */
-@ConfigurationProperties(OpenAiAudioSpeechProperties.CONFIG_PREFIX)
-public class OpenAiAudioSpeechProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.audio.speech";
-
- public static final String DEFAULT_SPEECH_MODEL = OpenAiAudioApi.TtsModel.TTS_1.getValue();
-
- private static final Float SPEED = 1.0f;
-
- private static final OpenAiAudioApi.SpeechRequest.Voice VOICE = OpenAiAudioApi.SpeechRequest.Voice.ALLOY;
-
- private static final OpenAiAudioApi.SpeechRequest.AudioResponseFormat DEFAULT_RESPONSE_FORMAT = OpenAiAudioApi.SpeechRequest.AudioResponseFormat.MP3;
-
- /**
- * Enable OpenAI audio speech model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private OpenAiAudioSpeechOptions options = OpenAiAudioSpeechOptions.builder()
- .model(DEFAULT_SPEECH_MODEL)
- .responseFormat(DEFAULT_RESPONSE_FORMAT)
- .voice(VOICE)
- .speed(SPEED)
- .build();
-
- public OpenAiAudioSpeechOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiAudioSpeechOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionProperties.java
deleted file mode 100644
index eb4a967f0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAudioTranscriptionProperties.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.openai.OpenAiAudioTranscriptionOptions;
-import org.springframework.ai.openai.api.OpenAiAudioApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-@ConfigurationProperties(OpenAiAudioTranscriptionProperties.CONFIG_PREFIX)
-public class OpenAiAudioTranscriptionProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.audio.transcription";
-
- public static final String DEFAULT_TRANSCRIPTION_MODEL = OpenAiAudioApi.WhisperModel.WHISPER_1.getValue();
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- private static final OpenAiAudioApi.TranscriptResponseFormat DEFAULT_RESPONSE_FORMAT = OpenAiAudioApi.TranscriptResponseFormat.TEXT;
-
- /**
- * Enable OpenAI audio transcription model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private OpenAiAudioTranscriptionOptions options = OpenAiAudioTranscriptionOptions.builder()
- .model(DEFAULT_TRANSCRIPTION_MODEL)
- .temperature(DEFAULT_TEMPERATURE.floatValue())
- .responseFormat(DEFAULT_RESPONSE_FORMAT)
- .build();
-
- public OpenAiAudioTranscriptionOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiAudioTranscriptionOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfiguration.java
deleted file mode 100644
index 1d6ddc51a..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfiguration.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.openai.autoconfigure;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import io.micrometer.observation.ObservationRegistry;
-import org.jetbrains.annotations.NotNull;
-
-import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.observation.ChatModelObservationConvention;
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
-import org.springframework.ai.image.observation.ImageModelObservationConvention;
-import org.springframework.ai.model.SimpleApiKey;
-import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
-import org.springframework.ai.model.function.FunctionCallbackResolver;
-import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.ai.openai.OpenAiAudioSpeechModel;
-import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiEmbeddingModel;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.OpenAiModerationModel;
-import org.springframework.ai.openai.api.OpenAiApi;
-import org.springframework.ai.openai.api.OpenAiAudioApi;
-import org.springframework.ai.openai.api.OpenAiImageApi;
-import org.springframework.ai.openai.api.OpenAiModerationApi;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.ResponseErrorHandler;
-import org.springframework.web.client.RestClient;
-import org.springframework.web.reactive.function.client.WebClient;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for OpenAI.
- *
- * @author Christian Tzolov
- * @author Stefan Vassilev
- * @author Thomas Vitale
- * @author Ilayaperumal Gopinathan
- */
-@AutoConfiguration(after = { RestClientAutoConfiguration.class, WebClientAutoConfiguration.class,
- SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class })
-@ConditionalOnClass(OpenAiApi.class)
-@EnableConfigurationProperties({ OpenAiConnectionProperties.class, OpenAiChatProperties.class,
- OpenAiEmbeddingProperties.class, OpenAiImageProperties.class, OpenAiAudioTranscriptionProperties.class,
- OpenAiAudioSpeechProperties.class, OpenAiModerationProperties.class })
-@ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, ToolCallingAutoConfiguration.class })
-public class OpenAiAutoConfiguration {
-
- private static @NotNull ResolvedConnectionProperties resolveConnectionProperties(
- OpenAiParentProperties commonProperties, OpenAiParentProperties modelProperties, String modelType) {
-
- String baseUrl = StringUtils.hasText(modelProperties.getBaseUrl()) ? modelProperties.getBaseUrl()
- : commonProperties.getBaseUrl();
- String apiKey = StringUtils.hasText(modelProperties.getApiKey()) ? modelProperties.getApiKey()
- : commonProperties.getApiKey();
- String projectId = StringUtils.hasText(modelProperties.getProjectId()) ? modelProperties.getProjectId()
- : commonProperties.getProjectId();
- String organizationId = StringUtils.hasText(modelProperties.getOrganizationId())
- ? modelProperties.getOrganizationId() : commonProperties.getOrganizationId();
-
- Map> connectionHeaders = new HashMap<>();
- if (StringUtils.hasText(projectId)) {
- connectionHeaders.put("OpenAI-Project", List.of(projectId));
- }
- if (StringUtils.hasText(organizationId)) {
- connectionHeaders.put("OpenAI-Organization", List.of(organizationId));
- }
-
- Assert.hasText(baseUrl,
- "OpenAI base URL must be set. Use the connection property: spring.ai.openai.base-url or spring.ai.openai."
- + modelType + ".base-url property.");
- Assert.hasText(apiKey,
- "OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai."
- + modelType + ".api-key property.");
-
- return new ResolvedConnectionProperties(baseUrl, apiKey, CollectionUtils.toMultiValueMap(connectionHeaders));
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OpenAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OpenAiChatModel openAiChatModel(OpenAiConnectionProperties commonProperties,
- OpenAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
- ObjectProvider webClientBuilderProvider, ToolCallingManager toolCallingManager,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var openAiApi = openAiApi(chatProperties, commonProperties,
- restClientBuilderProvider.getIfAvailable(RestClient::builder),
- webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "chat");
-
- var chatModel = OpenAiChatModel.builder()
- .openAiApi(openAiApi)
- .defaultOptions(chatProperties.getOptions())
- .toolCallingManager(toolCallingManager)
- .retryTemplate(retryTemplate)
- .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
- .build();
-
- observationConvention.ifAvailable(chatModel::setObservationConvention);
-
- return chatModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OpenAiEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OpenAiEmbeddingModel openAiEmbeddingModel(OpenAiConnectionProperties commonProperties,
- OpenAiEmbeddingProperties embeddingProperties, ObjectProvider restClientBuilderProvider,
- ObjectProvider webClientBuilderProvider, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var openAiApi = openAiApi(embeddingProperties, commonProperties,
- restClientBuilderProvider.getIfAvailable(RestClient::builder),
- webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "embedding");
-
- var embeddingModel = new OpenAiEmbeddingModel(openAiApi, embeddingProperties.getMetadataMode(),
- embeddingProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(embeddingModel::setObservationConvention);
-
- return embeddingModel;
- }
-
- private OpenAiApi openAiApi(OpenAiChatProperties chatProperties, OpenAiConnectionProperties commonProperties,
- RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
- ResponseErrorHandler responseErrorHandler, String modelType) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, chatProperties,
- modelType);
-
- return OpenAiApi.builder()
- .baseUrl(resolved.baseUrl())
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .completionsPath(chatProperties.getCompletionsPath())
- .embeddingsPath(OpenAiEmbeddingProperties.DEFAULT_EMBEDDINGS_PATH)
- .restClientBuilder(restClientBuilder)
- .webClientBuilder(webClientBuilder)
- .responseErrorHandler(responseErrorHandler)
- .build();
- }
-
- private OpenAiApi openAiApi(OpenAiEmbeddingProperties embeddingProperties,
- OpenAiConnectionProperties commonProperties, RestClient.Builder restClientBuilder,
- WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler, String modelType) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, embeddingProperties,
- modelType);
-
- return OpenAiApi.builder()
- .baseUrl(resolved.baseUrl())
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .completionsPath(OpenAiChatProperties.DEFAULT_COMPLETIONS_PATH)
- .embeddingsPath(embeddingProperties.getEmbeddingsPath())
- .restClientBuilder(restClientBuilder)
- .webClientBuilder(webClientBuilder)
- .responseErrorHandler(responseErrorHandler)
- .build();
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OpenAiImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OpenAiImageModel openAiImageModel(OpenAiConnectionProperties commonProperties,
- OpenAiImageProperties imageProperties, ObjectProvider restClientBuilderProvider,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, imageProperties, "image");
-
- var openAiImageApi = OpenAiImageApi.builder()
- .baseUrl(resolved.baseUrl())
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder))
- .responseErrorHandler(responseErrorHandler)
- .build();
- var imageModel = new OpenAiImageModel(openAiImageApi, imageProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(imageModel::setObservationConvention);
-
- return imageModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OpenAiAudioTranscriptionProperties.CONFIG_PREFIX, name = "enabled",
- havingValue = "true", matchIfMissing = true)
- public OpenAiAudioTranscriptionModel openAiAudioTranscriptionModel(OpenAiConnectionProperties commonProperties,
- OpenAiAudioTranscriptionProperties transcriptionProperties, RetryTemplate retryTemplate,
- ObjectProvider restClientBuilderProvider,
- ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, transcriptionProperties,
- "transcription");
-
- var openAiAudioApi = OpenAiAudioApi.builder()
- .baseUrl(resolved.baseUrl())
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder))
- .webClientBuilder(webClientBuilderProvider.getIfAvailable(WebClient::builder))
- .responseErrorHandler(responseErrorHandler)
- .build();
-
- return new OpenAiAudioTranscriptionModel(openAiAudioApi, transcriptionProperties.getOptions(), retryTemplate);
-
- }
-
- @Bean
- @ConditionalOnMissingBean
- public OpenAiModerationModel openAiModerationClient(OpenAiConnectionProperties commonProperties,
- OpenAiModerationProperties moderationProperties, RetryTemplate retryTemplate,
- ObjectProvider restClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, moderationProperties,
- "moderation");
-
- var openAiModerationApi = OpenAiModerationApi.builder()
- .baseUrl(resolved.baseUrl)
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder))
- .responseErrorHandler(responseErrorHandler)
- .build();
- return new OpenAiModerationModel(openAiModerationApi, retryTemplate)
- .withDefaultOptions(moderationProperties.getOptions());
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = OpenAiAudioSpeechProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public OpenAiAudioSpeechModel openAiAudioSpeechClient(OpenAiConnectionProperties commonProperties,
- OpenAiAudioSpeechProperties speechProperties, RetryTemplate retryTemplate,
- ObjectProvider restClientBuilderProvider,
- ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) {
-
- ResolvedConnectionProperties resolved = resolveConnectionProperties(commonProperties, speechProperties,
- "speach");
-
- var openAiAudioApi = OpenAiAudioApi.builder()
- .baseUrl(resolved.baseUrl())
- .apiKey(new SimpleApiKey(resolved.apiKey()))
- .headers(resolved.headers())
- .restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder))
- .webClientBuilder(webClientBuilderProvider.getIfAvailable(WebClient::builder))
- .responseErrorHandler(responseErrorHandler)
- .build();
-
- return new OpenAiAudioSpeechModel(openAiAudioApi, speechProperties.getOptions());
- }
-
- @Bean
- @ConditionalOnMissingBean
- public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
- DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
- manager.setApplicationContext(context);
- return manager;
- }
-
- private record ResolvedConnectionProperties(String baseUrl, String apiKey, MultiValueMap headers) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatProperties.java
deleted file mode 100644
index 3cef1db88..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiChatProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-@ConfigurationProperties(OpenAiChatProperties.CONFIG_PREFIX)
-public class OpenAiChatProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.chat";
-
- public static final String DEFAULT_CHAT_MODEL = "gpt-4o-mini";
-
- public static final String DEFAULT_COMPLETIONS_PATH = "/v1/chat/completions";
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- /**
- * Enable OpenAI chat model.
- */
- private boolean enabled = true;
-
- private String completionsPath = DEFAULT_COMPLETIONS_PATH;
-
- @NestedConfigurationProperty
- private OpenAiChatOptions options = OpenAiChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public OpenAiChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getCompletionsPath() {
- return this.completionsPath;
- }
-
- public void setCompletionsPath(String completionsPath) {
- this.completionsPath = completionsPath;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiConnectionProperties.java
deleted file mode 100644
index 41f00e303..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiConnectionProperties.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(OpenAiConnectionProperties.CONFIG_PREFIX)
-public class OpenAiConnectionProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai";
-
- public static final String DEFAULT_BASE_URL = "https://api.openai.com";
-
- public OpenAiConnectionProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingProperties.java
deleted file mode 100644
index c70e82c66..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiEmbeddingProperties.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.openai.OpenAiEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-@ConfigurationProperties(OpenAiEmbeddingProperties.CONFIG_PREFIX)
-public class OpenAiEmbeddingProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.embedding";
-
- public static final String DEFAULT_EMBEDDING_MODEL = "text-embedding-ada-002";
-
- public static final String DEFAULT_EMBEDDINGS_PATH = "/v1/embeddings";
-
- /**
- * Enable OpenAI embedding model.
- */
- private boolean enabled = true;
-
- private MetadataMode metadataMode = MetadataMode.EMBED;
-
- private String embeddingsPath = DEFAULT_EMBEDDINGS_PATH;
-
- @NestedConfigurationProperty
- private OpenAiEmbeddingOptions options = OpenAiEmbeddingOptions.builder().model(DEFAULT_EMBEDDING_MODEL).build();
-
- public OpenAiEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiEmbeddingOptions options) {
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getEmbeddingsPath() {
- return this.embeddingsPath;
- }
-
- public void setEmbeddingsPath(String embeddingsPath) {
- this.embeddingsPath = embeddingsPath;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageProperties.java
deleted file mode 100644
index ac7b11c80..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiImageProperties.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.openai.OpenAiImageOptions;
-import org.springframework.ai.openai.api.OpenAiImageApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * OpenAI Image autoconfiguration properties.
- *
- * @author Thomas Vitale
- * @since 0.8.0
- */
-@ConfigurationProperties(OpenAiImageProperties.CONFIG_PREFIX)
-public class OpenAiImageProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.image";
-
- public static final String DEFAULT_IMAGE_MODEL = OpenAiImageApi.ImageModel.DALL_E_3.getValue();
-
- /**
- * Enable OpenAI image model.
- */
- private boolean enabled = true;
-
- /**
- * Options for OpenAI Image API.
- */
- @NestedConfigurationProperty
- private OpenAiImageOptions options = OpenAiImageOptions.builder().model(DEFAULT_IMAGE_MODEL).build();
-
- public OpenAiImageOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiImageOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationProperties.java
deleted file mode 100644
index a268f93bd..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiModerationProperties.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.springframework.ai.openai.OpenAiModerationOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * OpenAI Moderation autoconfiguration properties.
- *
- * @author Ahmed Yousri
- * @since 0.9.0
- */
-@ConfigurationProperties(OpenAiModerationProperties.CONFIG_PREFIX)
-public class OpenAiModerationProperties extends OpenAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.openai.moderation";
-
- /**
- * Options for OpenAI Moderation API.
- */
- @NestedConfigurationProperty
- private OpenAiModerationOptions options = OpenAiModerationOptions.builder().build();
-
- public OpenAiModerationOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(OpenAiModerationOptions options) {
- this.options = options;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiParentProperties.java
deleted file mode 100644
index 9b3f29a0e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/openai/autoconfigure/OpenAiParentProperties.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-/**
- * Internal parent properties for the OpenAI properties.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-class OpenAiParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- private String projectId;
-
- private String organizationId;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
- public String getProjectId() {
- return this.projectId;
- }
-
- public void setProjectId(String projectId) {
- this.projectId = projectId;
- }
-
- public String getOrganizationId() {
- return this.organizationId;
- }
-
- public void setOrganizationId(String organizationId) {
- this.organizationId = organizationId;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlAutoConfiguration.java
deleted file mode 100644
index ae0092ee8..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlAutoConfiguration.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.model.postgresml.autoconfigure;
-
-import org.springframework.ai.postgresml.PostgresMlEmbeddingModel;
-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.ConditionalOnProperty;
-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 PostgresMlEmbeddingModel.
- *
- * @author Utkarsh Srivastava
- * @author Christian Tzolov
- */
-@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class)
-@ConditionalOnClass(PostgresMlEmbeddingModel.class)
-@EnableConfigurationProperties(PostgresMlEmbeddingProperties.class)
-public class PostgresMlAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = PostgresMlEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public PostgresMlEmbeddingModel postgresMlEmbeddingModel(JdbcTemplate jdbcTemplate,
- PostgresMlEmbeddingProperties embeddingProperties) {
-
- return new PostgresMlEmbeddingModel(jdbcTemplate, embeddingProperties.getOptions(),
- embeddingProperties.isCreateExtension());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingProperties.java
deleted file mode 100644
index 5cd3ca0df..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingProperties.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.model.postgresml.autoconfigure;
-
-import java.util.Map;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.postgresml.PostgresMlEmbeddingModel;
-import org.springframework.ai.postgresml.PostgresMlEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.util.Assert;
-
-/**
- * Configuration properties for Postgres ML.
- *
- * @author Utkarsh Srivastava
- * @author Christian Tzolov
- */
-@ConfigurationProperties(PostgresMlEmbeddingProperties.CONFIG_PREFIX)
-public class PostgresMlEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.postgresml.embedding";
-
- /**
- * Enable Postgres ML embedding model.
- */
- private boolean enabled = true;
-
- /**
- * Create the extensions required for embedding
- */
- private boolean createExtension;
-
- @NestedConfigurationProperty
- private PostgresMlEmbeddingOptions options = PostgresMlEmbeddingOptions.builder()
- .transformer(PostgresMlEmbeddingModel.DEFAULT_TRANSFORMER_MODEL)
- .vectorType(PostgresMlEmbeddingModel.VectorType.PG_ARRAY)
- .kwargs(Map.of())
- .metadataMode(MetadataMode.EMBED)
- .build();
-
- public PostgresMlEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(PostgresMlEmbeddingOptions options) {
- Assert.notNull(options, "options must not be null.");
- Assert.notNull(options.getTransformer(), "transformer must not be null.");
- Assert.notNull(options.getVectorType(), "vectorType must not be null.");
- Assert.notNull(options.getKwargs(), "kwargs must not be null.");
- Assert.notNull(options.getMetadataMode(), "metadataMode must not be null.");
-
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public boolean isCreateExtension() {
- return this.createExtension;
- }
-
- public void setCreateExtension(boolean createExtension) {
- this.createExtension = createExtension;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfiguration.java
deleted file mode 100644
index a48870d9d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfiguration.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.observation.ChatModelObservationConvention;
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
-import org.springframework.ai.image.observation.ImageModelObservationConvention;
-import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
-import org.springframework.ai.model.function.FunctionCallbackResolver;
-import org.springframework.ai.qianfan.QianFanChatModel;
-import org.springframework.ai.qianfan.QianFanEmbeddingModel;
-import org.springframework.ai.qianfan.QianFanImageModel;
-import org.springframework.ai.qianfan.api.QianFanApi;
-import org.springframework.ai.qianfan.api.QianFanImageApi;
-import org.springframework.beans.factory.ObjectProvider;
-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.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.ResponseErrorHandler;
-import org.springframework.web.client.RestClient;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for QianFan Chat, Embedding, and Image
- * Models.
- *
- * @author Geng Rong
- */
-@AutoConfiguration(after = { RestClientAutoConfiguration.class, SpringAiRetryAutoConfiguration.class })
-@ConditionalOnClass(QianFanApi.class)
-@EnableConfigurationProperties({ QianFanConnectionProperties.class, QianFanChatProperties.class,
- QianFanEmbeddingProperties.class, QianFanImageProperties.class })
-public class QianFanAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = QianFanChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public QianFanChatModel qianFanChatModel(QianFanConnectionProperties commonProperties,
- QianFanChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var qianFanApi = qianFanApi(chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- chatProperties.getApiKey(), commonProperties.getApiKey(), chatProperties.getSecretKey(),
- commonProperties.getSecretKey(), restClientBuilderProvider.getIfAvailable(RestClient::builder),
- responseErrorHandler);
-
- var chatModel = new QianFanChatModel(qianFanApi, chatProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(chatModel::setObservationConvention);
-
- return chatModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = QianFanEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public QianFanEmbeddingModel qianFanEmbeddingModel(QianFanConnectionProperties commonProperties,
- QianFanEmbeddingProperties embeddingProperties,
- ObjectProvider restClientBuilderProvider, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var qianFanApi = qianFanApi(embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- embeddingProperties.getApiKey(), commonProperties.getApiKey(), embeddingProperties.getSecretKey(),
- commonProperties.getSecretKey(), restClientBuilderProvider.getIfAvailable(RestClient::builder),
- responseErrorHandler);
-
- var embeddingModel = new QianFanEmbeddingModel(qianFanApi, embeddingProperties.getMetadataMode(),
- embeddingProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(embeddingModel::setObservationConvention);
-
- return embeddingModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = QianFanImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public QianFanImageModel qianFanImageModel(QianFanConnectionProperties commonProperties,
- QianFanImageProperties imageProperties, ObjectProvider restClientBuilderProvider,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- String apiKey = StringUtils.hasText(imageProperties.getApiKey()) ? imageProperties.getApiKey()
- : commonProperties.getApiKey();
-
- String secretKey = StringUtils.hasText(imageProperties.getSecretKey()) ? imageProperties.getSecretKey()
- : commonProperties.getSecretKey();
-
- String baseUrl = StringUtils.hasText(imageProperties.getBaseUrl()) ? imageProperties.getBaseUrl()
- : commonProperties.getBaseUrl();
-
- Assert.hasText(apiKey, "QianFan API key must be set. Use the property: spring.ai.qianfan.api-key");
- Assert.hasText(secretKey, "QianFan secret key must be set. Use the property: spring.ai.qianfan.secret-key");
- Assert.hasText(baseUrl, "QianFan base URL must be set. Use the property: spring.ai.qianfan.base-url");
-
- var qianFanImageApi = new QianFanImageApi(baseUrl, apiKey, secretKey,
- restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
-
- var imageModel = new QianFanImageModel(qianFanImageApi, imageProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(imageModel::setObservationConvention);
-
- return imageModel;
- }
-
- private QianFanApi qianFanApi(String baseUrl, String commonBaseUrl, String apiKey, String commonApiKey,
- String secretKey, String commonSecretKey, RestClient.Builder restClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
-
- String resolvedBaseUrl = StringUtils.hasText(baseUrl) ? baseUrl : commonBaseUrl;
- Assert.hasText(resolvedBaseUrl, "QianFan base URL must be set");
-
- String resolvedApiKey = StringUtils.hasText(apiKey) ? apiKey : commonApiKey;
- Assert.hasText(resolvedApiKey, "QianFan API key must be set");
-
- String resolvedSecretKey = StringUtils.hasText(secretKey) ? secretKey : commonSecretKey;
- Assert.hasText(resolvedSecretKey, "QianFan Secret key must be set");
-
- return new QianFanApi(resolvedBaseUrl, resolvedApiKey, resolvedSecretKey, restClientBuilder,
- responseErrorHandler);
- }
-
- @Bean
- @ConditionalOnMissingBean
- public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
- DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
- manager.setApplicationContext(context);
- return manager;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanChatProperties.java
deleted file mode 100644
index 9ca6b16cb..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanChatProperties.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import org.springframework.ai.qianfan.QianFanChatOptions;
-import org.springframework.ai.qianfan.api.QianFanApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for QianFan chat model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(QianFanChatProperties.CONFIG_PREFIX)
-public class QianFanChatProperties extends QianFanParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.qianfan.chat";
-
- public static final String DEFAULT_CHAT_MODEL = QianFanApi.ChatModel.ERNIE_Speed_8K.value;
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- /**
- * Enable QianFan chat client.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private QianFanChatOptions options = QianFanChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public QianFanChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(QianFanChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanConnectionProperties.java
deleted file mode 100644
index e26662ef6..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanConnectionProperties.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import org.springframework.ai.qianfan.api.QianFanConstants;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(QianFanConnectionProperties.CONFIG_PREFIX)
-public class QianFanConnectionProperties extends QianFanParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.qianfan";
-
- public static final String DEFAULT_BASE_URL = QianFanConstants.DEFAULT_BASE_URL;
-
- public QianFanConnectionProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanEmbeddingProperties.java
deleted file mode 100644
index d71bd12b3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanEmbeddingProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.qianfan.QianFanEmbeddingOptions;
-import org.springframework.ai.qianfan.api.QianFanApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for QianFan embedding model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(QianFanEmbeddingProperties.CONFIG_PREFIX)
-public class QianFanEmbeddingProperties extends QianFanParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.qianfan.embedding";
-
- /**
- * Enable QianFan embedding client.
- */
- private boolean enabled = true;
-
- private MetadataMode metadataMode = MetadataMode.EMBED;
-
- @NestedConfigurationProperty
- private QianFanEmbeddingOptions options = QianFanEmbeddingOptions.builder()
- .model(QianFanApi.DEFAULT_EMBEDDING_MODEL)
- .build();
-
- public QianFanEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(QianFanEmbeddingOptions options) {
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanImageProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanImageProperties.java
deleted file mode 100644
index 3341b760b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanImageProperties.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import org.springframework.ai.qianfan.QianFanImageOptions;
-import org.springframework.ai.qianfan.api.QianFanImageApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * QianFan Image autoconfiguration properties.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(QianFanImageProperties.CONFIG_PREFIX)
-public class QianFanImageProperties extends QianFanParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.qianfan.image";
-
- public static final String DEFAULT_IMAGE_MODEL = QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue();
-
- /**
- * Enable QianFan image model.
- */
- private boolean enabled = true;
-
- /**
- * Options for QianFan Image API.
- */
- @NestedConfigurationProperty
- private QianFanImageOptions options = QianFanImageOptions.builder().model(DEFAULT_IMAGE_MODEL).build();
-
- public QianFanImageOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(QianFanImageOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanParentProperties.java
deleted file mode 100644
index 424cbadc1..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanParentProperties.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-/**
- * @author Geng Rong
- */
-class QianFanParentProperties {
-
- private String apiKey;
-
- private String secretKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getSecretKey() {
- return this.secretKey;
- }
-
- public void setSecretKey(String secretKey) {
- this.secretKey = secretKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiConnectionProperties.java
deleted file mode 100644
index d1ec0261d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiConnectionProperties.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-import org.springframework.ai.stabilityai.api.StabilityAiApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(StabilityAiConnectionProperties.CONFIG_PREFIX)
-public class StabilityAiConnectionProperties extends StabilityAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.stabilityai";
-
- public static final String DEFAULT_BASE_URL = StabilityAiApi.DEFAULT_BASE_URL;
-
- public StabilityAiConnectionProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java
deleted file mode 100644
index 7c143a941..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageAutoConfiguration.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-import org.springframework.ai.stabilityai.StabilityAiImageModel;
-import org.springframework.ai.stabilityai.api.StabilityAiApi;
-import org.springframework.beans.factory.ObjectProvider;
-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.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestClient;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for StabilityAI Image Model.
- *
- * @author Mark Pollack
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@AutoConfiguration(after = { RestClientAutoConfiguration.class })
-@ConditionalOnClass(StabilityAiApi.class)
-@EnableConfigurationProperties({ StabilityAiConnectionProperties.class, StabilityAiImageProperties.class })
-public class StabilityAiImageAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public StabilityAiApi stabilityAiApi(StabilityAiConnectionProperties commonProperties,
- StabilityAiImageProperties imageProperties, ObjectProvider restClientBuilderProvider) {
-
- 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,
- restClientBuilderProvider.getIfAvailable(RestClient::builder));
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = StabilityAiImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public StabilityAiImageModel stabilityAiImageModel(StabilityAiApi stabilityAiApi,
- StabilityAiImageProperties stabilityAiImageProperties) {
- return new StabilityAiImageModel(stabilityAiApi, stabilityAiImageProperties.getOptions());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageProperties.java
deleted file mode 100644
index dc78af948..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImageProperties.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for Stability AI image model.
- *
- * @author Mark Pollack
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(StabilityAiImageProperties.CONFIG_PREFIX)
-public class StabilityAiImageProperties extends StabilityAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.stabilityai.image";
-
- /**
- * Enable Stability image model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private StabilityAiImageOptions options = StabilityAiImageOptions.builder().build(); // stable-diffusion-v1-6
-
- // is
- // default
- // model
-
- public StabilityAiImageOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(StabilityAiImageOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiParentProperties.java
deleted file mode 100644
index f75cc2b09..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiParentProperties.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-/**
- * Internal parent properties for the StabilityAI properties.
- *
- * @author Mark Pollack
- * @since 0.8.0
- */
-class StabilityAiParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfiguration.java
deleted file mode 100644
index b653b67d7..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfiguration.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.tool.autoconfigure;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.ai.tool.ToolCallbackProvider;
-import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor;
-import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor;
-import org.springframework.ai.tool.resolution.DelegatingToolCallbackResolver;
-import org.springframework.ai.tool.resolution.SpringBeanToolCallbackResolver;
-import org.springframework.ai.tool.resolution.StaticToolCallbackResolver;
-import org.springframework.ai.tool.resolution.ToolCallbackResolver;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.support.GenericApplicationContext;
-
-/**
- * Auto-configuration for common tool calling features of {@link ChatModel}.
- *
- * @author Thomas Vitale
- * @author Christian Tzolov
- * @since 1.0.0
- */
-@AutoConfiguration
-@ConditionalOnClass(ChatModel.class)
-public class ToolCallingAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- ToolCallbackResolver toolCallbackResolver(GenericApplicationContext applicationContext,
- List functionCallbacks, List tcbProviders) {
-
- List allFunctionAndToolCallbacks = new ArrayList<>(functionCallbacks);
- tcbProviders.stream().map(pr -> List.of(pr.getToolCallbacks())).forEach(allFunctionAndToolCallbacks::addAll);
-
- var staticToolCallbackResolver = new StaticToolCallbackResolver(allFunctionAndToolCallbacks);
-
- var springBeanToolCallbackResolver = SpringBeanToolCallbackResolver.builder()
- .applicationContext(applicationContext)
- .build();
-
- return new DelegatingToolCallbackResolver(List.of(staticToolCallbackResolver, springBeanToolCallbackResolver));
- }
-
- @Bean
- @ConditionalOnMissingBean
- ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
- return new DefaultToolExecutionExceptionProcessor(false);
- }
-
- @Bean
- @ConditionalOnMissingBean
- ToolCallingManager toolCallingManager(ToolCallbackResolver toolCallbackResolver,
- ToolExecutionExceptionProcessor toolExecutionExceptionProcessor,
- ObjectProvider observationRegistry) {
- return ToolCallingManager.builder()
- .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
- .toolCallbackResolver(toolCallbackResolver)
- .toolExecutionExceptionProcessor(toolExecutionExceptionProcessor)
- .build();
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/transformers/autoconfigure/TransformersEmbeddingModelProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/transformers/autoconfigure/TransformersEmbeddingModelProperties.java
deleted file mode 100644
index a4535f8ac..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/transformers/autoconfigure/TransformersEmbeddingModelProperties.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.model.transformers.autoconfigure;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
-
-import org.springframework.ai.document.Document;
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.transformers.TransformersEmbeddingModel;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for the Transformer Embedding model.
- *
- * @author Christian Tzolov
- */
-@ConfigurationProperties(TransformersEmbeddingModelProperties.CONFIG_PREFIX)
-public class TransformersEmbeddingModelProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.embedding.transformer";
-
- public static final String DEFAULT_CACHE_DIRECTORY = new File(System.getProperty("java.io.tmpdir"),
- "spring-ai-onnx-generative")
- .getAbsolutePath();
-
- @NestedConfigurationProperty
- private final Tokenizer tokenizer = new Tokenizer();
-
- /**
- * Controls caching of remote, large resources to local file system.
- */
- @NestedConfigurationProperty
- private final Cache cache = new Cache();
-
- @NestedConfigurationProperty
- private final Onnx onnx = new Onnx();
-
- /**
- * Enable the Transformer Embedding model.
- */
- private boolean enabled = true;
-
- /**
- * Specifies what parts of the {@link Document}'s content and metadata will be used
- * for computing the embeddings. Applicable for the
- * {@link TransformersEmbeddingModel#embed(Document)} method only. Has no effect on
- * the {@link TransformersEmbeddingModel#embed(String)} or
- * {@link TransformersEmbeddingModel#embed(List)}. Defaults to
- * {@link MetadataMode#NONE}.
- */
- private MetadataMode metadataMode = MetadataMode.NONE;
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public Cache getCache() {
- return this.cache;
- }
-
- public Onnx getOnnx() {
- return this.onnx;
- }
-
- public Tokenizer getTokenizer() {
- return this.tokenizer;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- /**
- * Configurations for the {@link HuggingFaceTokenizer} used to convert sentences into
- * tokens.
- */
- public static class Tokenizer {
-
- /**
- * URI of a pre-trained HuggingFaceTokenizer created by the ONNX engine (e.g.
- * tokenizer.json).
- */
- private String uri = TransformersEmbeddingModel.DEFAULT_ONNX_TOKENIZER_URI;
-
- /**
- * HuggingFaceTokenizer options such as 'addSpecialTokens', 'modelMaxLength',
- * 'truncation', 'padding', 'maxLength', 'stride' and 'padToMultipleOf'. Leave
- * empty to fall back to the defaults.
- */
- @NestedConfigurationProperty
- private Map options = new HashMap<>();
-
- public String getUri() {
- return this.uri;
- }
-
- public void setUri(String uri) {
- this.uri = uri;
- }
-
- public Map getOptions() {
- return this.options;
- }
-
- public void setOptions(Map options) {
- this.options = options;
- }
-
- }
-
- public static class Cache {
-
- /**
- * Enable the Resource caching.
- */
- private boolean enabled = true;
-
- /**
- * Resource cache directory. Used to cache remote resources, such as the ONNX
- * models, to the local file system. Applicable only for cache.enabled == true.
- * Defaults to {java.io.tmpdir}/spring-ai-onnx-generative.
- */
- private String directory = DEFAULT_CACHE_DIRECTORY;
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getDirectory() {
- return this.directory;
- }
-
- public void setDirectory(String directory) {
- this.directory = directory;
- }
-
- }
-
- public static class Onnx {
-
- /**
- * Existing, pre-trained ONNX generative. Commonly exported from
- * https://sbert.net/docs/pretrained_models.html. Defaults to
- * sentence-transformers/all-MiniLM-L6-v2.
- */
- private String modelUri = TransformersEmbeddingModel.DEFAULT_ONNX_MODEL_URI;
-
- /**
- * Defaults to: 'last_hidden_state'.
- */
- private String modelOutputName = TransformersEmbeddingModel.DEFAULT_MODEL_OUTPUT_NAME;
-
- /**
- * Run on a GPU or with another provider (optional).
- * https://onnxruntime.ai/docs/get-started/with-java.html#run-on-a-gpu-or-with-another-provider-optional
- *
- * The GPU device ID to execute on. Only applicable if >= 0. Ignored otherwise.
- */
- private int gpuDeviceId = -1;
-
- public String getModelUri() {
- return this.modelUri;
- }
-
- public void setModelUri(String modelUri) {
- this.modelUri = modelUri;
- }
-
- public int getGpuDeviceId() {
- return this.gpuDeviceId;
- }
-
- public void setGpuDeviceId(int gpuDeviceId) {
- this.gpuDeviceId = gpuDeviceId;
- }
-
- public String getModelOutputName() {
- return this.modelOutputName;
- }
-
- public void setModelOutputName(String modelOutputName) {
- this.modelOutputName = modelOutputName;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingAutoConfiguration.java
deleted file mode 100644
index ab57b118f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingAutoConfiguration.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.embedding;
-
-import java.io.IOException;
-
-import com.google.cloud.vertexai.VertexAI;
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.vertexai.embedding.VertexAiEmbeddingConnectionDetails;
-import org.springframework.ai.vertexai.embedding.multimodal.VertexAiMultimodalEmbeddingModel;
-import org.springframework.ai.vertexai.embedding.text.VertexAiTextEmbeddingModel;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-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.retry.support.RetryTemplate;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-
-/**
- * Auto-configuration for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @author Mark Pollack
- * @since 1.0.0
- */
-@AutoConfiguration(after = { SpringAiRetryAutoConfiguration.class })
-@ConditionalOnClass({ VertexAI.class, VertexAiTextEmbeddingModel.class })
-@EnableConfigurationProperties({ VertexAiEmbeddingConnectionProperties.class, VertexAiTextEmbeddingProperties.class,
- VertexAiMultimodalEmbeddingProperties.class })
-@ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class })
-public class VertexAiEmbeddingAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public VertexAiEmbeddingConnectionDetails connectionDetails(
- VertexAiEmbeddingConnectionProperties connectionProperties) {
-
- Assert.hasText(connectionProperties.getProjectId(), "Vertex AI project-id must be set!");
- Assert.hasText(connectionProperties.getLocation(), "Vertex AI location must be set!");
-
- var connectionBuilder = VertexAiEmbeddingConnectionDetails.builder()
- .projectId(connectionProperties.getProjectId())
- .location(connectionProperties.getLocation());
-
- if (StringUtils.hasText(connectionProperties.getApiEndpoint())) {
- connectionBuilder.apiEndpoint(connectionProperties.getApiEndpoint());
- }
-
- return connectionBuilder.build();
-
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = VertexAiTextEmbeddingProperties.CONFIG_PREFIX, name = "enabled",
- havingValue = "true", matchIfMissing = true)
- public VertexAiTextEmbeddingModel textEmbedding(VertexAiEmbeddingConnectionDetails connectionDetails,
- VertexAiTextEmbeddingProperties textEmbeddingProperties, RetryTemplate retryTemplate,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var embeddingModel = new VertexAiTextEmbeddingModel(connectionDetails, textEmbeddingProperties.getOptions(),
- retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(embeddingModel::setObservationConvention);
-
- return embeddingModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = VertexAiMultimodalEmbeddingProperties.CONFIG_PREFIX, name = "enabled",
- havingValue = "true", matchIfMissing = true)
- public VertexAiMultimodalEmbeddingModel multimodalEmbedding(VertexAiEmbeddingConnectionDetails connectionDetails,
- VertexAiMultimodalEmbeddingProperties multimodalEmbeddingProperties) throws IOException {
-
- return new VertexAiMultimodalEmbeddingModel(connectionDetails, multimodalEmbeddingProperties.getOptions());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingConnectionProperties.java
deleted file mode 100644
index a55cb3774..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiEmbeddingConnectionProperties.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.embedding;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.core.io.Resource;
-
-/**
- * Configuration properties for Vertex AI Embedding.
- *
- * @author Christian Tzolov
- * @since 1.0.0
- */
-@ConfigurationProperties(VertexAiEmbeddingConnectionProperties.CONFIG_PREFIX)
-public class VertexAiEmbeddingConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.embedding";
-
- /**
- * Vertex AI Gemini project ID.
- */
- private String projectId;
-
- /**
- * Vertex AI Gemini location.
- */
- private String location;
-
- /**
- * URI to Vertex AI Gemini credentials (optional)
- */
- private Resource credentialsUri;
-
- /**
- * Vertex AI Gemini API endpoint.
- */
- private String apiEndpoint;
-
- public String getProjectId() {
- return this.projectId;
- }
-
- public void setProjectId(String projectId) {
- this.projectId = projectId;
- }
-
- public String getLocation() {
- return this.location;
- }
-
- public void setLocation(String location) {
- this.location = location;
- }
-
- public Resource getCredentialsUri() {
- return this.credentialsUri;
- }
-
- public void setCredentialsUri(Resource credentialsUri) {
- this.credentialsUri = credentialsUri;
- }
-
- public String getApiEndpoint() {
- return this.apiEndpoint;
- }
-
- public void setApiEndpoint(String apiEndpoint) {
- this.apiEndpoint = apiEndpoint;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiMultimodalEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiMultimodalEmbeddingProperties.java
deleted file mode 100644
index e1bb8e2c3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiMultimodalEmbeddingProperties.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.embedding;
-
-import org.springframework.ai.vertexai.embedding.multimodal.VertexAiMultimodalEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @since 1.0.0
- */
-@ConfigurationProperties(VertexAiMultimodalEmbeddingProperties.CONFIG_PREFIX)
-public class VertexAiMultimodalEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.embedding.multimodal";
-
- private boolean enabled = true;
-
- /**
- * Vertex AI Text Embedding API options.
- */
- private VertexAiMultimodalEmbeddingOptions options = VertexAiMultimodalEmbeddingOptions.builder()
- .model(VertexAiMultimodalEmbeddingOptions.DEFAULT_MODEL_NAME)
- .build();
-
- public VertexAiMultimodalEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(VertexAiMultimodalEmbeddingOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingProperties.java
deleted file mode 100644
index 737a4a6bc..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/embedding/VertexAiTextEmbeddingProperties.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.embedding;
-
-import org.springframework.ai.vertexai.embedding.text.VertexAiTextEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @since 1.0.0
- */
-@ConfigurationProperties(VertexAiTextEmbeddingProperties.CONFIG_PREFIX)
-public class VertexAiTextEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.embedding.text";
-
- private boolean enabled = true;
-
- /**
- * Vertex AI Text Embedding API options.
- */
- private VertexAiTextEmbeddingOptions options = VertexAiTextEmbeddingOptions.builder()
- .taskType(VertexAiTextEmbeddingOptions.TaskType.RETRIEVAL_DOCUMENT)
- .model(VertexAiTextEmbeddingOptions.DEFAULT_MODEL_NAME)
- .build();
-
- public VertexAiTextEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(VertexAiTextEmbeddingOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiAutoConfiguration.java
deleted file mode 100644
index 4a059efa2..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiAutoConfiguration.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.gemini;
-
-import java.io.IOException;
-
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.cloud.vertexai.VertexAI;
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.observation.ChatModelObservationConvention;
-import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatModel;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-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.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
-
-/**
- * Auto-configuration for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @author Soby Chacko
- * @author Mark Pollack
- * @since 1.0.0
- */
-@AutoConfiguration(after = { SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class })
-@ConditionalOnClass({ VertexAI.class, VertexAiGeminiChatModel.class })
-@EnableConfigurationProperties({ VertexAiGeminiChatProperties.class, VertexAiGeminiConnectionProperties.class })
-@ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class, ToolCallingAutoConfiguration.class })
-public class VertexAiGeminiAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public VertexAI vertexAi(VertexAiGeminiConnectionProperties connectionProperties) throws IOException {
-
- Assert.hasText(connectionProperties.getProjectId(), "Vertex AI project-id must be set!");
- Assert.hasText(connectionProperties.getLocation(), "Vertex AI location must be set!");
- Assert.notNull(connectionProperties.getTransport(), "Vertex AI transport must be set!");
-
- var vertexAIBuilder = new VertexAI.Builder().setProjectId(connectionProperties.getProjectId())
- .setLocation(connectionProperties.getLocation())
- .setTransport(com.google.cloud.vertexai.Transport.valueOf(connectionProperties.getTransport().name()));
-
- if (StringUtils.hasText(connectionProperties.getApiEndpoint())) {
- vertexAIBuilder.setApiEndpoint(connectionProperties.getApiEndpoint());
- }
- if (!CollectionUtils.isEmpty(connectionProperties.getScopes())) {
- vertexAIBuilder.setScopes(connectionProperties.getScopes());
- }
-
- if (connectionProperties.getCredentialsUri() != null) {
- GoogleCredentials credentials = GoogleCredentials
- .fromStream(connectionProperties.getCredentialsUri().getInputStream());
-
- vertexAIBuilder.setCredentials(credentials);
- }
- return vertexAIBuilder.build();
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = VertexAiGeminiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public VertexAiGeminiChatModel vertexAiGeminiChat(VertexAI vertexAi, VertexAiGeminiChatProperties chatProperties,
- ToolCallingManager toolCallingManager, ApplicationContext context, RetryTemplate retryTemplate,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- VertexAiGeminiChatModel chatModel = VertexAiGeminiChatModel.builder()
- .vertexAI(vertexAi)
- .defaultOptions(chatProperties.getOptions())
- .toolCallingManager(toolCallingManager)
- .retryTemplate(retryTemplate)
- .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
- .build();
-
- observationConvention.ifAvailable(chatModel::setObservationConvention);
-
- return chatModel;
- }
-
- /**
- * Because of the OPEN_API_SCHEMA type, the FunctionCallbackResolver instance must
- * different from the other JSON schema types.
- */
- // private FunctionCallbackResolver springAiFunctionManager(ApplicationContext
- // context) {
- // DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
- // manager.setSchemaType(SchemaType.OPEN_API_SCHEMA);
- // manager.setApplicationContext(context);
- // return manager;
- // }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatProperties.java
deleted file mode 100644
index 1770e5ed1..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatProperties.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.gemini;
-
-import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatModel;
-import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * Configuration properties for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(VertexAiGeminiChatProperties.CONFIG_PREFIX)
-public class VertexAiGeminiChatProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.gemini.chat";
-
- public static final String DEFAULT_MODEL = VertexAiGeminiChatModel.ChatModel.GEMINI_2_0_FLASH.getValue();
-
- /**
- * Vertex AI Gemini API generative options.
- */
- private VertexAiGeminiChatOptions options = VertexAiGeminiChatOptions.builder()
- .temperature(0.7)
- .candidateCount(1)
- .model(DEFAULT_MODEL)
- .build();
-
- public VertexAiGeminiChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(VertexAiGeminiChatOptions options) {
- this.options = options;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiConnectionProperties.java
deleted file mode 100644
index 7adf07052..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiConnectionProperties.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.model.vertexai.autoconfigure.gemini;
-
-import java.util.List;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.core.io.Resource;
-
-/**
- * Configuration properties for Vertex AI Gemini Chat.
- *
- * @author Christian Tzolov
- * @since 0.8.0
- */
-@ConfigurationProperties(VertexAiGeminiConnectionProperties.CONFIG_PREFIX)
-public class VertexAiGeminiConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.vertex.ai.gemini";
-
- /**
- * Vertex AI Gemini project ID.
- */
- private String projectId;
-
- /**
- * Vertex AI Gemini location.
- */
- private String location;
-
- /**
- * URI to Vertex AI Gemini credentials (optional)
- */
- private Resource credentialsUri;
-
- /**
- * Vertex AI Gemini API endpoint.
- */
- private String apiEndpoint;
-
- /**
- *
- */
- private List scopes = List.of();
-
- private Transport transport = Transport.GRPC;
-
- public String getProjectId() {
- return this.projectId;
- }
-
- public void setProjectId(String projectId) {
- this.projectId = projectId;
- }
-
- public String getLocation() {
- return this.location;
- }
-
- public void setLocation(String location) {
- this.location = location;
- }
-
- public Resource getCredentialsUri() {
- return this.credentialsUri;
- }
-
- public void setCredentialsUri(Resource credentialsUri) {
- this.credentialsUri = credentialsUri;
- }
-
- public String getApiEndpoint() {
- return this.apiEndpoint;
- }
-
- public void setApiEndpoint(String apiEndpoint) {
- this.apiEndpoint = apiEndpoint;
- }
-
- public List getScopes() {
- return this.scopes;
- }
-
- public void setScopes(List scopes) {
- this.scopes = scopes;
- }
-
- public Transport getTransport() {
- return this.transport;
- }
-
- public void setTransport(Transport transport) {
- this.transport = transport;
- }
-
- public enum Transport {
-
- /** When used, the clients will send REST requests to the backing service. */
- REST,
- /**
- * When used, the clients will send gRPC to the backing service. This is usually
- * more efficient and is the default transport.
- */
- GRPC
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiChatProperties.java
deleted file mode 100644
index cff01d7fc..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiChatProperties.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.model.watsonxai.autoconfigure;
-
-import java.util.List;
-
-import org.springframework.ai.watsonx.WatsonxAiChatOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Chat properties for Watsonx.AI Chat.
- *
- * @author Christian Tzolov
- * @author Alexandros Pappas
- * @since 1.0.0
- */
-@ConfigurationProperties(WatsonxAiChatProperties.CONFIG_PREFIX)
-public class WatsonxAiChatProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.watsonx.ai.chat";
-
- /**
- * Enable Watsonx.AI chat model.
- */
- private boolean enabled = true;
-
- /**
- * Watsonx AI generative options.
- */
- @NestedConfigurationProperty
- private WatsonxAiChatOptions options = WatsonxAiChatOptions.builder()
- .model("google/flan-ul2")
- .temperature(0.7)
- .topP(1.0)
- .topK(50)
- .decodingMethod("greedy")
- .maxNewTokens(20)
- .minNewTokens(0)
- .repetitionPenalty(1.0)
- .stopSequences(List.of())
- .build();
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public WatsonxAiChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(WatsonxAiChatOptions options) {
- this.options = options;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiConnectionProperties.java
deleted file mode 100644
index a49ab048c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiConnectionProperties.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.model.watsonxai.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * WatsonX.ai connection autoconfiguration properties.
- *
- * @author Pablo Sanchidrian Herrera
- * @author John Jario Moreno Rojas
- * @since 1.0.0
- */
-@ConfigurationProperties(WatsonxAiConnectionProperties.CONFIG_PREFIX)
-public class WatsonxAiConnectionProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.watsonx.ai";
-
- private String baseUrl = "https://us-south.ml.cloud.ibm.com/";
-
- private String streamEndpoint = "ml/v1/text/generation_stream?version=2023-05-29";
-
- private String textEndpoint = "ml/v1/text/generation?version=2023-05-29";
-
- private String embeddingEndpoint = "ml/v1/text/embeddings?version=2023-05-29";
-
- private String projectId;
-
- private String IAMToken;
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
- public String getStreamEndpoint() {
- return this.streamEndpoint;
- }
-
- public void setStreamEndpoint(String streamEndpoint) {
- this.streamEndpoint = streamEndpoint;
- }
-
- public String getTextEndpoint() {
- return this.textEndpoint;
- }
-
- public void setTextEndpoint(String textEndpoint) {
- this.textEndpoint = textEndpoint;
- }
-
- public String getEmbeddingEndpoint() {
- return this.embeddingEndpoint;
- }
-
- public void setEmbeddingEndpoint(String embeddingEndpoint) {
- this.embeddingEndpoint = embeddingEndpoint;
- }
-
- public String getProjectId() {
- return this.projectId;
- }
-
- public void setProjectId(String projectId) {
- this.projectId = projectId;
- }
-
- public String getIAMToken() {
- return this.IAMToken;
- }
-
- public void setIAMToken(String IAMToken) {
- this.IAMToken = IAMToken;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiEmbeddingProperties.java
deleted file mode 100644
index 1691951f0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/watsonxai/autoconfigure/WatsonxAiEmbeddingProperties.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.model.watsonxai.autoconfigure;
-
-import org.springframework.ai.watsonx.WatsonxAiEmbeddingOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Watsonx.ai Embedding autoconfiguration properties.
- *
- * @author Pablo Sanchidrian Herrera
- * @since 1.0.0
- */
-@ConfigurationProperties(WatsonxAiEmbeddingProperties.CONFIG_PREFIX)
-public class WatsonxAiEmbeddingProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.watsonx.ai.embedding";
-
- /**
- * Enable Watsonx.ai embedding model.
- */
- private boolean enabled = true;
-
- /**
- * Client lever Watsonx.ai embedding options. Use this property to configure the
- * model. The null values are ignored defaulting to the defaults.
- */
- @NestedConfigurationProperty
- private WatsonxAiEmbeddingOptions options = WatsonxAiEmbeddingOptions.create()
- .withModel(WatsonxAiEmbeddingOptions.DEFAULT_MODEL);
-
- public String getModel() {
- return this.options.getModel();
- }
-
- public void setModel(String model) {
- this.options.setModel(model);
- }
-
- public WatsonxAiEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiAutoConfiguration.java
deleted file mode 100644
index db27df995..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiAutoConfiguration.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-import java.util.List;
-
-import io.micrometer.observation.ObservationRegistry;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.observation.ChatModelObservationConvention;
-import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
-import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.model.function.FunctionCallbackResolver;
-import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
-import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel;
-import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
-import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
-import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
-import org.springframework.beans.factory.ObjectProvider;
-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.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.ResponseErrorHandler;
-import org.springframework.web.client.RestClient;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for ZhiPuAI.
- *
- * @author Geng Rong
- */
-@AutoConfiguration(after = { RestClientAutoConfiguration.class, SpringAiRetryAutoConfiguration.class })
-@ConditionalOnClass(ZhiPuAiApi.class)
-@EnableConfigurationProperties({ ZhiPuAiConnectionProperties.class, ZhiPuAiChatProperties.class,
- ZhiPuAiEmbeddingProperties.class, ZhiPuAiImageProperties.class })
-public class ZhiPuAiAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ZhiPuAiChatProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public ZhiPuAiChatModel zhiPuAiChatModel(ZhiPuAiConnectionProperties commonProperties,
- ZhiPuAiChatProperties chatProperties, ObjectProvider restClientBuilderProvider,
- List toolFunctionCallbacks, FunctionCallbackResolver functionCallbackResolver,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var zhiPuAiApi = zhiPuAiApi(chatProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- chatProperties.getApiKey(), commonProperties.getApiKey(),
- restClientBuilderProvider.getIfAvailable(RestClient::builder), responseErrorHandler);
-
- var chatModel = new ZhiPuAiChatModel(zhiPuAiApi, chatProperties.getOptions(), functionCallbackResolver,
- toolFunctionCallbacks, retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(chatModel::setObservationConvention);
-
- return chatModel;
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ZhiPuAiEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public ZhiPuAiEmbeddingModel zhiPuAiEmbeddingModel(ZhiPuAiConnectionProperties commonProperties,
- ZhiPuAiEmbeddingProperties embeddingProperties, RestClient.Builder restClientBuilder,
- RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler,
- ObjectProvider observationRegistry,
- ObjectProvider observationConvention) {
-
- var zhiPuAiApi = zhiPuAiApi(embeddingProperties.getBaseUrl(), commonProperties.getBaseUrl(),
- embeddingProperties.getApiKey(), commonProperties.getApiKey(), restClientBuilder, responseErrorHandler);
-
- var embeddingModel = new ZhiPuAiEmbeddingModel(zhiPuAiApi, embeddingProperties.getMetadataMode(),
- embeddingProperties.getOptions(), retryTemplate,
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
-
- observationConvention.ifAvailable(embeddingModel::setObservationConvention);
-
- return embeddingModel;
- }
-
- private ZhiPuAiApi zhiPuAiApi(String baseUrl, String commonBaseUrl, String apiKey, String commonApiKey,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
-
- String resolvedBaseUrl = StringUtils.hasText(baseUrl) ? baseUrl : commonBaseUrl;
- Assert.hasText(resolvedBaseUrl, "ZhiPuAI base URL must be set");
-
- String resolvedApiKey = StringUtils.hasText(apiKey) ? apiKey : commonApiKey;
- Assert.hasText(resolvedApiKey, "ZhiPuAI API key must be set");
-
- return new ZhiPuAiApi(resolvedBaseUrl, resolvedApiKey, restClientBuilder, responseErrorHandler);
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = ZhiPuAiImageProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
- matchIfMissing = true)
- public ZhiPuAiImageModel zhiPuAiImageModel(ZhiPuAiConnectionProperties commonProperties,
- ZhiPuAiImageProperties imageProperties, RestClient.Builder restClientBuilder, RetryTemplate retryTemplate,
- ResponseErrorHandler responseErrorHandler) {
-
- String apiKey = StringUtils.hasText(imageProperties.getApiKey()) ? imageProperties.getApiKey()
- : commonProperties.getApiKey();
-
- String baseUrl = StringUtils.hasText(imageProperties.getBaseUrl()) ? imageProperties.getBaseUrl()
- : commonProperties.getBaseUrl();
-
- Assert.hasText(apiKey, "ZhiPuAI API key must be set");
- Assert.hasText(baseUrl, "ZhiPuAI base URL must be set");
-
- var zhiPuAiImageApi = new ZhiPuAiImageApi(baseUrl, apiKey, restClientBuilder, responseErrorHandler);
-
- return new ZhiPuAiImageModel(zhiPuAiImageApi, imageProperties.getOptions(), retryTemplate);
- }
-
- @Bean
- @ConditionalOnMissingBean
- public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
- DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
- manager.setApplicationContext(context);
- return manager;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatProperties.java
deleted file mode 100644
index e5f44f475..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiChatProperties.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
-import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for ZhiPuAI chat model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(ZhiPuAiChatProperties.CONFIG_PREFIX)
-public class ZhiPuAiChatProperties extends ZhiPuAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.zhipuai.chat";
-
- public static final String DEFAULT_CHAT_MODEL = ZhiPuAiApi.ChatModel.GLM_4_Air.value;
-
- private static final Double DEFAULT_TEMPERATURE = 0.7;
-
- /**
- * Enable ZhiPuAI chat model.
- */
- private boolean enabled = true;
-
- @NestedConfigurationProperty
- private ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
- .model(DEFAULT_CHAT_MODEL)
- .temperature(DEFAULT_TEMPERATURE)
- .build();
-
- public ZhiPuAiChatOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(ZhiPuAiChatOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiConnectionProperties.java
deleted file mode 100644
index 3e3aee144..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiConnectionProperties.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(ZhiPuAiConnectionProperties.CONFIG_PREFIX)
-public class ZhiPuAiConnectionProperties extends ZhiPuAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.zhipuai";
-
- public static final String DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas";
-
- public ZhiPuAiConnectionProperties() {
- super.setBaseUrl(DEFAULT_BASE_URL);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingProperties.java
deleted file mode 100644
index 300b0a23d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiEmbeddingProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions;
-import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for ZhiPuAI embedding model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(ZhiPuAiEmbeddingProperties.CONFIG_PREFIX)
-public class ZhiPuAiEmbeddingProperties extends ZhiPuAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.zhipuai.embedding";
-
- public static final String DEFAULT_EMBEDDING_MODEL = ZhiPuAiApi.EmbeddingModel.Embedding_2.value;
-
- /**
- * Enable ZhiPuAI embedding model.
- */
- private boolean enabled = true;
-
- private MetadataMode metadataMode = MetadataMode.EMBED;
-
- @NestedConfigurationProperty
- private ZhiPuAiEmbeddingOptions options = ZhiPuAiEmbeddingOptions.builder().model(DEFAULT_EMBEDDING_MODEL).build();
-
- public ZhiPuAiEmbeddingOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(ZhiPuAiEmbeddingOptions options) {
- this.options = options;
- }
-
- public MetadataMode getMetadataMode() {
- return this.metadataMode;
- }
-
- public void setMetadataMode(MetadataMode metadataMode) {
- this.metadataMode = metadataMode;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageProperties.java
deleted file mode 100644
index bfcceee5e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiImageProperties.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Configuration properties for ZhiPuAI chat model.
- *
- * @author Geng Rong
- */
-@ConfigurationProperties(ZhiPuAiImageProperties.CONFIG_PREFIX)
-public class ZhiPuAiImageProperties extends ZhiPuAiParentProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.zhipuai.image";
-
- /**
- * Enable ZhiPuAI image model.
- */
- private boolean enabled = true;
-
- /**
- * Options for ZhiPuAI Image API.
- */
- @NestedConfigurationProperty
- private ZhiPuAiImageOptions options = ZhiPuAiImageOptions.builder().build();
-
- public ZhiPuAiImageOptions getOptions() {
- return this.options;
- }
-
- public void setOptions(ZhiPuAiImageOptions options) {
- this.options = options;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiParentProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiParentProperties.java
deleted file mode 100644
index 5a8e3f193..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/model/zhipuai/autoconfigure/ZhiPuAiParentProperties.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.model.zhipuai.autoconfigure;
-
-/**
- * @author Geng Rong
- */
-class ZhiPuAiParentProperties {
-
- private String apiKey;
-
- private String baseUrl;
-
- public String getApiKey() {
- return this.apiKey;
- }
-
- public void setApiKey(String apiKey) {
- this.apiKey = apiKey;
- }
-
- public String getBaseUrl() {
- return this.baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
deleted file mode 100644
index f44ec9613..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.retry.autoconfigure;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.retry.NonTransientAiException;
-import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.retry.TransientAiException;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.lang.NonNull;
-import org.springframework.retry.RetryCallback;
-import org.springframework.retry.RetryContext;
-import org.springframework.retry.RetryListener;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StreamUtils;
-import org.springframework.web.client.ResponseErrorHandler;
-
-/**
- * {@link AutoConfiguration Auto-configuration} for AI Retry.
- *
- * @author Christian Tzolov
- */
-@AutoConfiguration
-@ConditionalOnClass(RetryUtils.class)
-@EnableConfigurationProperties({ SpringAiRetryProperties.class })
-public class SpringAiRetryAutoConfiguration {
-
- private static final Logger logger = LoggerFactory.getLogger(SpringAiRetryAutoConfiguration.class);
-
- @Bean
- @ConditionalOnMissingBean
- public RetryTemplate retryTemplate(SpringAiRetryProperties properties) {
- return RetryTemplate.builder()
- .maxAttempts(properties.getMaxAttempts())
- .retryOn(TransientAiException.class)
- .exponentialBackoff(properties.getBackoff().getInitialInterval(), properties.getBackoff().getMultiplier(),
- properties.getBackoff().getMaxInterval())
- .withListener(new RetryListener() {
-
- @Override
- public void onError(RetryContext context,
- RetryCallback callback, Throwable throwable) {
- logger.warn("Retry error. Retry count:" + context.getRetryCount(), throwable);
- }
- })
- .build();
- }
-
- @Bean
- @ConditionalOnMissingBean
- public ResponseErrorHandler responseErrorHandler(SpringAiRetryProperties properties) {
-
- return new ResponseErrorHandler() {
-
- @Override
- public boolean hasError(@NonNull ClientHttpResponse response) throws IOException {
- return response.getStatusCode().isError();
- }
-
- @Override
- public void handleError(@NonNull ClientHttpResponse response) throws IOException {
- if (response.getStatusCode().isError()) {
- String error = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
- String message = String.format("%s - %s", response.getStatusCode().value(), error);
-
- // Explicitly configured transient codes
- if (properties.getOnHttpCodes().contains(response.getStatusCode().value())) {
- throw new TransientAiException(message);
- }
-
- // onClientErrors - If true, do not throw a NonTransientAiException,
- // and do not attempt retry for 4xx client error codes, false by
- // default.
- if (!properties.isOnClientErrors() && response.getStatusCode().is4xxClientError()) {
- throw new NonTransientAiException(message);
- }
-
- // Explicitly configured non-transient codes
- if (!CollectionUtils.isEmpty(properties.getExcludeOnHttpCodes())
- && properties.getExcludeOnHttpCodes().contains(response.getStatusCode().value())) {
- throw new NonTransientAiException(message);
- }
- throw new TransientAiException(message);
- }
- }
- };
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryProperties.java
deleted file mode 100644
index 385103a94..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryProperties.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.retry.autoconfigure;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * Properties for AI Retry.
- *
- * @author Christian Tzolov
- */
-@ConfigurationProperties(SpringAiRetryProperties.CONFIG_PREFIX)
-public class SpringAiRetryProperties {
-
- public static final String CONFIG_PREFIX = "spring.ai.retry";
-
- /**
- * Maximum number of retry attempts.
- */
- private int maxAttempts = 10;
-
- /**
- * Exponential Backoff properties.
- */
- @NestedConfigurationProperty
- private Backoff backoff = new Backoff();
-
- /**
- * If false, throw a NonTransientAiException, and do not attempt retry for 4xx client
- * error codes. False by default. If true, throw a TransientAiException, and attempt
- * retry for 4xx client.
- */
- private boolean onClientErrors = false;
-
- /**
- * List of HTTP status codes that should not trigger a retry (e.g. throw
- * NonTransientAiException).
- */
- private List excludeOnHttpCodes = new ArrayList<>();
-
- /**
- * List of HTTP status codes that should trigger a retry.
- */
- private List onHttpCodes = new ArrayList<>();
-
- public int getMaxAttempts() {
- return this.maxAttempts;
- }
-
- public void setMaxAttempts(int maxAttempts) {
- this.maxAttempts = maxAttempts;
- }
-
- public Backoff getBackoff() {
- return this.backoff;
- }
-
- public List getExcludeOnHttpCodes() {
- return this.excludeOnHttpCodes;
- }
-
- public void setExcludeOnHttpCodes(List onHttpCodes) {
- this.excludeOnHttpCodes = onHttpCodes;
- }
-
- public boolean isOnClientErrors() {
- return this.onClientErrors;
- }
-
- public void setOnClientErrors(boolean onClientErrors) {
- this.onClientErrors = onClientErrors;
- }
-
- public List getOnHttpCodes() {
- return this.onHttpCodes;
- }
-
- public void setOnHttpCodes(List onHttpCodes) {
- this.onHttpCodes = onHttpCodes;
- }
-
- /**
- * Exponential Backoff properties.
- */
- public static class Backoff {
-
- /**
- * Initial sleep duration.
- */
- private Duration initialInterval = Duration.ofMillis(2000);
-
- /**
- * Backoff interval multiplier.
- */
- private int multiplier = 5;
-
- /**
- * Maximum backoff duration.
- */
- private Duration maxInterval = Duration.ofMillis(3 * 60000);
-
- public Duration getInitialInterval() {
- return this.initialInterval;
- }
-
- public void setInitialInterval(Duration initialInterval) {
- this.initialInterval = initialInterval;
- }
-
- public int getMultiplier() {
- return this.multiplier;
- }
-
- public void setMultiplier(int multiplier) {
- this.multiplier = multiplier;
- }
-
- public Duration getMaxInterval() {
- return this.maxInterval;
- }
-
- public void setMaxInterval(Duration maxInterval) {
- this.maxInterval = maxInterval;
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
deleted file mode 100644
index 3a94062ad..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "groups": [
- {
- "name": "spring.ai.azure.openai.chat.options.enhancements",
- "type": "com.azure.ai.openai.models.AzureChatEnhancementConfiguration",
- "sourceType": "org.springframework.ai.azure.openai.AzureOpenAiChatOptions",
- "sourceMethod": "getEnhancements()"
- },
- {
- "name": "spring.ai.mistralai.chat.options.tool-choice",
- "type": "org.springframework.ai.mistralai.api.MistralAiApi$ChatCompletionRequest$ToolChoice",
- "sourceType": "org.springframework.ai.mistralai.MistralAiChatOptions"
- },
- {
- "name": "spring.ai.openai.chat.output-audio",
- "type": "org.springframework.ai.openai.api.OpenAiApi$ChatCompletionRequest$AudioParameters",
- "sourceType": "org.springframework.ai.openai.OpenAiChatOptions"
- }
- ],
- "properties": [
- {
- "name": "spring.ai.azure.openai.chat.options.enhancements.grounding",
- "type": "com.azure.ai.openai.models.AzureChatGroundingEnhancementConfiguration",
- "sourceType": "com.azure.ai.openai.models.AzureChatEnhancementConfiguration"
- },
- {
- "name": "spring.ai.azure.openai.chat.options.enhancements.ocr",
- "type": "com.azure.ai.openai.models.AzureChatOCREnhancementConfiguration",
- "sourceType": "com.azure.ai.openai.models.AzureChatEnhancementConfiguration"
- },
- {
- "name": "spring.ai.openai.chat.output-audio.voice",
- "type": "org.springframework.ai.openai.api.OpenAiApi$ChatCompletionRequest$AudioParameters$Voice",
- "sourceType": "org.springframework.ai.openai.api.OpenAiApi$ChatCompletionRequest$AudioParameters"
- },
- {
- "name": "spring.ai.openai.chat.output-audio.format",
- "type": "org.springframework.ai.openai.api.OpenAiApi$ChatCompletionRequest$AudioParameters$AudioResponseFormat",
- "sourceType": "org.springframework.ai.openai.api.OpenAiApi$ChatCompletionRequest$AudioParameters"
- }
- ],
- "hints": []
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
deleted file mode 100644
index 7cb623cc6..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# 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.
-#
-org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration
-org.springframework.ai.model.oci.genai.autoconfigure.OCIGenAiAutoConfiguration
-org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration
-org.springframework.ai.model.transformers.autoconfigure.TransformersEmbeddingModelAutoConfiguration
-org.springframework.ai.model.huggingface.autoconfigure.HuggingfaceChatAutoConfiguration
-gemini.org.springframework.ai.model.vertexai.autoconfigure.VertexAiGeminiAutoConfiguration
-cohere.org.springframework.ai.model.bedrock.autoconfigure.BedrockCohereEmbeddingAutoConfiguration
-titan.org.springframework.ai.model.bedrock.autoconfigure.BedrockTitanEmbeddingAutoConfiguration
-converse.org.springframework.ai.model.bedrock.autoconfigure.BedrockConverseProxyChatAutoConfiguration
-org.springframework.ai.model.chat.observation.autoconfigure.ChatObservationAutoConfiguration
-org.springframework.ai.model.embedding.observation.autoconfigure.EmbeddingObservationAutoConfiguration
-org.springframework.ai.model.image.observation.autoconfigure.ImageObservationAutoConfiguration
-org.springframework.ai.model.ollama.autoconfigure.OllamaAutoConfiguration
-org.springframework.ai.model.mistralai.autoconfigure.MistralAiAutoConfiguration
-org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration
-org.springframework.ai.model.postgresml.autoconfigure.PostgresMlAutoConfiguration
-org.springframework.ai.model.anthropic.autoconfigure.AnthropicAutoConfiguration
-org.springframework.ai.model.watsonxai.autoconfigure.WatsonxAiAutoConfiguration
-org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiAutoConfiguration
-org.springframework.ai.model.chat.client.autoconfigure.ChatClientAutoConfiguration
-org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration
-org.springframework.ai.autoconfigure.chat.client.ChatClientAutoConfiguration
-org.springframework.ai.model.moonshot.autoconfigure.MoonshotAutoConfiguration
-org.springframework.ai.model.qianfan.autoconfigure.QianFanAutoConfiguration
-org.springframework.ai.model.minimax.autoconfigure.MiniMaxAutoConfiguration
-embedding.org.springframework.ai.model.vertexai.autoconfigure.VertexAiEmbeddingAutoConfiguration
-cassandra.org.springframework.ai.model.chat.memory.autoconfigure.CassandraChatMemoryAutoConfiguration
-org.springframework.ai.autoconfigure.vectorstore.observation.VectorStoreObservationAutoConfiguration
-org.springframework.ai.autoconfigure.vectorstore.couchbase.CouchbaseSearchVectorStoreAutoConfiguration
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/chat/observation/ChatObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/chat/observation/ChatObservationAutoConfigurationTests.java
deleted file mode 100644
index d16010981..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/chat/observation/ChatObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.chat.observation;
-
-import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
-import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import io.opentelemetry.api.OpenTelemetry;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelMeterObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationHandler;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ChatObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class ChatObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(ChatObservationAutoConfiguration.class));
-
- @Test
- void meterObservationHandlerEnabled() {
- this.contextRunner.withBean(CompositeMeterRegistry.class)
- .run(context -> assertThat(context).hasSingleBean(ChatModelMeterObservationHandler.class));
- }
-
- @Test
- void meterObservationHandlerDisabled() {
- this.contextRunner.run(context -> assertThat(context).doesNotHaveBean(ChatModelMeterObservationHandler.class));
- }
-
- @Test
- void promptFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationFilter.class));
- }
-
- @Test
- void promptHandlerDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void promptHandlerEnabled() {
- this.contextRunner
- .withBean(OtelTracer.class, OpenTelemetry.noop().getTracer("test"), new OtelCurrentTraceContext(), null)
- .withPropertyValues("spring.ai.chat.observations.include-prompt=true")
- .run(context -> assertThat(context).hasSingleBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void promptHandlerDisabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.observations.include-prompt=true")
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void completionFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationFilter.class));
- }
-
- @Test
- void completionHandlerDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationHandler.class));
- }
-
- @Test
- void completionHandlerEnabled() {
- this.contextRunner
- .withBean(OtelTracer.class, OpenTelemetry.noop().getTracer("test"), new OtelCurrentTraceContext(), null)
- .withPropertyValues("spring.ai.chat.observations.include-completion=true")
- .run(context -> assertThat(context).hasSingleBean(ChatModelCompletionObservationHandler.class));
- }
-
- @Test
- void completionHandlerDisabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.observations.include-completion=true")
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationHandler.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/embedding/observation/EmbeddingObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/embedding/observation/EmbeddingObservationAutoConfigurationTests.java
deleted file mode 100644
index 42037c9e0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/embedding/observation/EmbeddingObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.embedding.observation;
-
-import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.embedding.observation.EmbeddingModelMeterObservationHandler;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link EmbeddingObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class EmbeddingObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(EmbeddingObservationAutoConfiguration.class));
-
- @Test
- void meterObservationHandlerEnabled() {
- this.contextRunner.withBean(CompositeMeterRegistry.class)
- .run(context -> assertThat(context).hasSingleBean(EmbeddingModelMeterObservationHandler.class));
- }
-
- @Test
- void meterObservationHandlerDisabled() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(EmbeddingModelMeterObservationHandler.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/image/observation/ImageObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/image/observation/ImageObservationAutoConfigurationTests.java
deleted file mode 100644
index deb0a22ad..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/image/observation/ImageObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.image.observation;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.image.observation.ImageModelPromptContentObservationFilter;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ImageObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class ImageObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(ImageObservationAutoConfiguration.class));
-
- @Test
- void promptFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ImageModelPromptContentObservationFilter.class));
- }
-
- @Test
- void promptFilterEnabled() {
- this.contextRunner.withPropertyValues("spring.ai.image.observations.include-prompt=true")
- .run(context -> assertThat(context).hasSingleBean(ImageModelPromptContentObservationFilter.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicAutoConfigurationIT.java
deleted file mode 100644
index a46ff4022..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicAutoConfigurationIT.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.model.anthropic.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.anthropic.AnthropicChatModel;
-import org.springframework.ai.anthropic.AnthropicChatOptions;
-import org.springframework.ai.anthropic.api.AnthropicApi;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "ANTHROPIC_API_KEY", matches = ".*")
-public class AnthropicAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(AnthropicAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.anthropic.apiKey=" + System.getenv("ANTHROPIC_API_KEY"))
- .withConfiguration(AutoConfigurations.of(AnthropicAutoConfiguration.class));
-
- @Test
- void call() {
- this.contextRunner.run(context -> {
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void callWith8KResponseContext() {
- this.contextRunner
- .withPropertyValues("spring.ai.anthropic.beta-version=" + AnthropicApi.BETA_MAX_TOKENS,
- "spring.ai.anthropic.chat.options.model=" + AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getValue())
- .run(context -> {
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
- var optoins = AnthropicChatOptions.builder().maxTokens(8192).build();
- var response = chatModel.call(new Prompt("Tell me a joke", optoins));
- assertThat(response.getResult().getOutput().getText()).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void stream() {
- this.contextRunner.run(context -> {
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
-
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java
deleted file mode 100644
index ceb714227..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/AnthropicPropertiesTests.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.model.anthropic.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.anthropic.AnthropicChatModel;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link AnthropicChatProperties}, {@link AnthropicConnectionProperties}.
- */
-public class AnthropicPropertiesTests {
-
- @Test
- public void connectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.anthropic.base-url=TEST_BASE_URL",
- "spring.ai.anthropic.api-key=abc123",
- "spring.ai.anthropic.version=6666",
- "spring.ai.anthropic.beta-version=7777",
- "spring.ai.anthropic.chat.options.model=MODEL_XYZ",
- "spring.ai.anthropic.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, AnthropicAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(AnthropicChatProperties.class);
- var connectionProperties = context.getBean(AnthropicConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getVersion()).isEqualTo("6666");
- assertThat(connectionProperties.getBetaVersion()).isEqualTo("7777");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- // enabled is true by default
- assertThat(chatProperties.isEnabled()).isTrue();
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.anthropic.api-key=API_KEY",
- "spring.ai.anthropic.base-url=TEST_BASE_URL",
-
- "spring.ai.anthropic.chat.options.model=MODEL_XYZ",
- "spring.ai.anthropic.chat.options.max-tokens=123",
- "spring.ai.anthropic.chat.options.metadata.user-id=MyUserId",
- "spring.ai.anthropic.chat.options.stop_sequences=boza,koza",
-
- "spring.ai.anthropic.chat.options.temperature=0.55",
- "spring.ai.anthropic.chat.options.top-p=0.56",
- "spring.ai.anthropic.chat.options.top-k=100"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, AnthropicAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(AnthropicChatProperties.class);
- var connectionProperties = context.getBean(AnthropicConnectionProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getStopSequences()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
- assertThat(chatProperties.getOptions().getTopK()).isEqualTo(100);
-
- assertThat(chatProperties.getOptions().getMetadata().userId()).isEqualTo("MyUserId");
- });
- }
-
- @Test
- public void chatCompletionDisabled() {
-
- // It is enabled by default
- new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, AnthropicAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(AnthropicChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(AnthropicChatModel.class)).isNotEmpty();
- });
-
- // Explicitly enable the chat auto-configuration.
- new ApplicationContextRunner().withPropertyValues("spring.ai.anthropic.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, AnthropicAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(AnthropicChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(AnthropicChatModel.class)).isNotEmpty();
- });
-
- // Explicitly disable the chat auto-configuration.
- new ApplicationContextRunner().withPropertyValues("spring.ai.anthropic.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, AnthropicAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(AnthropicChatProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(AnthropicChatModel.class)).isEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java
deleted file mode 100644
index fb4e53e86..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.model.anthropic.autoconfigure.tool;
-
-import java.util.List;
-import java.util.function.Function;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.anthropic.AnthropicChatModel;
-import org.springframework.ai.anthropic.AnthropicChatOptions;
-import org.springframework.ai.anthropic.api.AnthropicApi;
-import org.springframework.ai.model.anthropic.autoconfigure.AnthropicAutoConfiguration;
-import org.springframework.ai.model.anthropic.autoconfigure.tool.MockWeatherService.Request;
-import org.springframework.ai.model.anthropic.autoconfigure.tool.MockWeatherService.Response;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "ANTHROPIC_API_KEY", matches = ".*")
-class FunctionCallWithFunctionBeanIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithFunctionBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.anthropic.apiKey=" + System.getenv("ANTHROPIC_API_KEY"))
- .withConfiguration(AutoConfigurations.of(AnthropicAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
-
- this.contextRunner
- .withPropertyValues(
- "spring.ai.anthropic.chat.options.model=" + AnthropicApi.ChatModel.CLAUDE_3_5_HAIKU.getValue())
- .run(context -> {
-
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
-
- var userMessage = new UserMessage(
- "What's the weather like in San Francisco, in Paris, France and in Tokyo, Japan? Return the temperature in Celsius.");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- AnthropicChatOptions.builder().toolNames("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- response = chatModel.call(new Prompt(List.of(userMessage),
- AnthropicChatOptions.builder().toolNames("weatherFunction3").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
-
- this.contextRunner
- .withPropertyValues(
- "spring.ai.anthropic.chat.options.model=" + AnthropicApi.ChatModel.CLAUDE_3_5_HAIKU.getValue())
- .run(context -> {
-
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
-
- var userMessage = new UserMessage(
- "What's the weather like in San Francisco, in Paris, France and in Tokyo, Japan? Return the temperature in Celsius.");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- ToolCallingChatOptions.builder().toolNames("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get the weather in location. Return temperature in 36°F or 36°C format.")
- public Function weatherFunction() {
- return new MockWeatherService();
- }
-
- // Relies on the Request's JsonClassDescription annotation to provide the
- // function description.
- @Bean
- public Function weatherFunction3() {
- MockWeatherService weatherService = new MockWeatherService();
- return (weatherService::apply);
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java
deleted file mode 100644
index 27b8d2dab..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.model.anthropic.autoconfigure.tool;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.anthropic.AnthropicChatModel;
-import org.springframework.ai.anthropic.AnthropicChatOptions;
-import org.springframework.ai.anthropic.api.AnthropicApi;
-import org.springframework.ai.model.anthropic.autoconfigure.AnthropicAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "ANTHROPIC_API_KEY", matches = ".*")
-public class FunctionCallWithPromptFunctionIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithPromptFunctionIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.anthropic.apiKey=" + System.getenv("ANTHROPIC_API_KEY"))
- .withConfiguration(AutoConfigurations.of(AnthropicAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.anthropic.chat.options.model=" + AnthropicApi.ChatModel.CLAUDE_3_5_HAIKU.getValue())
- .run(context -> {
-
- AnthropicChatModel chatModel = context.getBean(AnthropicChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, in Paris and in Tokyo? Return the temperature in Celsius.");
-
- var promptOptions = AnthropicChatOptions.builder()
- .toolCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location. Return temperature in 36°F or 36°C format.")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationIT.java
deleted file mode 100644
index 4cd87697c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure;
-
-import java.lang.reflect.Field;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
-
-import com.azure.ai.openai.OpenAIClient;
-import com.azure.ai.openai.OpenAIClientBuilder;
-import com.azure.ai.openai.implementation.OpenAIClientImpl;
-import com.azure.core.http.HttpHeader;
-import com.azure.core.http.HttpHeaderName;
-import com.azure.core.http.HttpMethod;
-import com.azure.core.http.HttpPipeline;
-import com.azure.core.http.HttpRequest;
-import com.azure.core.http.HttpResponse;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAIClientBuilderCustomizer;
-
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.azure.openai.AzureOpenAiAudioTranscriptionModel;
-import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
-import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.chat.prompt.SystemPromptTemplate;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.springframework.util.ReflectionUtils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- * @author Piotr Olaszewski
- * @author Soby Chacko
- * @author Manuel Andreo Garcia
- * @since 0.8.0
- */
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
-class AzureOpenAiAutoConfigurationIT {
-
- private static String CHAT_MODEL_NAME = "gpt-4o";
-
- private static String EMBEDDING_MODEL_NAME = "text-embedding-ada-002";
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.api-key=" + System.getenv("AZURE_OPENAI_API_KEY"),
- "spring.ai.azure.openai.endpoint=" + System.getenv("AZURE_OPENAI_ENDPOINT"),
-
- "spring.ai.azure.openai.chat.options.deployment-name=" + CHAT_MODEL_NAME,
- "spring.ai.azure.openai.chat.options.temperature=0.8",
- "spring.ai.azure.openai.chat.options.maxTokens=123",
-
- "spring.ai.azure.openai.embedding.options.deployment-name=" + EMBEDDING_MODEL_NAME,
- "spring.ai.azure.openai.audio.transcription.options.deployment-name=" + System.getenv("AZURE_OPENAI_TRANSCRIPTION_DEPLOYMENT_NAME")
- // @formatter:on
- ).withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class));
-
- private final Message systemMessage = new SystemPromptTemplate("""
- You are a helpful AI assistant. Your name is {name}.
- You are an AI assistant that helps people find information.
- Your name is {name}
- You should reply to the user's request with your name and also in the style of a {voice}.
- """).createMessage(Map.of("name", "Bob", "voice", "pirate"));
-
- private final UserMessage userMessage = new UserMessage(
- "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.");
-
- @Test
- void chatCompletion() {
- this.contextRunner.run(context -> {
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
- ChatResponse response = chatModel.call(new Prompt(List.of(this.userMessage, this.systemMessage)));
- assertThat(response.getResult().getOutput().getText()).contains("Blackbeard");
- });
- }
-
- @Test
- void httpRequestContainsUserAgentAndCustomHeaders() {
- this.contextRunner
- .withPropertyValues("spring.ai.azure.openai.custom-headers.foo=bar",
- "spring.ai.azure.openai.custom-headers.fizz=buzz")
- .run(context -> {
- OpenAIClientBuilder openAIClientBuilder = context.getBean(OpenAIClientBuilder.class);
- OpenAIClient openAIClient = openAIClientBuilder.buildClient();
- Field serviceClientField = ReflectionUtils.findField(OpenAIClient.class, "serviceClient");
- assertThat(serviceClientField).isNotNull();
- ReflectionUtils.makeAccessible(serviceClientField);
- OpenAIClientImpl oaci = (OpenAIClientImpl) ReflectionUtils.getField(serviceClientField, openAIClient);
- assertThat(oaci).isNotNull();
- HttpPipeline httpPipeline = oaci.getHttpPipeline();
- HttpResponse httpResponse = httpPipeline
- .send(new HttpRequest(HttpMethod.POST, new URI(System.getenv("AZURE_OPENAI_ENDPOINT")).toURL()))
- .block();
- assertThat(httpResponse).isNotNull();
- HttpHeader httpHeader = httpResponse.getRequest().getHeaders().get(HttpHeaderName.USER_AGENT);
- assertThat(httpHeader.getValue().startsWith("spring-ai azsdk-java-azure-ai-openai/")).isTrue();
- HttpHeader customHeader1 = httpResponse.getRequest().getHeaders().get("foo");
- assertThat(customHeader1.getValue()).isEqualTo("bar");
- HttpHeader customHeader2 = httpResponse.getRequest().getHeaders().get("fizz");
- assertThat(customHeader2.getValue()).isEqualTo("buzz");
- });
- }
-
- @Test
- void chatCompletionStreaming() {
- this.contextRunner.run(context -> {
-
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- Flux response = chatModel.stream(new Prompt(List.of(this.userMessage, this.systemMessage)));
-
- List responses = response.collectList().block();
- assertThat(responses.size()).isGreaterThan(10);
-
- String stitchedResponseContent = responses.stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(stitchedResponseContent).contains("Blackbeard");
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- AzureOpenAiEmbeddingModel embeddingModel = context.getBean(AzureOpenAiEmbeddingModel.class);
-
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1536);
- });
- }
-
- @Test
- @EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_TRANSCRIPTION_DEPLOYMENT_NAME", matches = ".+")
- void transcribe() {
- this.contextRunner.run(context -> {
- AzureOpenAiAudioTranscriptionModel transcriptionModel = context
- .getBean(AzureOpenAiAudioTranscriptionModel.class);
- Resource audioFile = new ClassPathResource("/speech/jfk.flac");
- String response = transcriptionModel.call(audioFile);
- assertThat(response).isEqualTo(
- "And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country.");
- });
- }
-
- @Test
- void chatActivation() {
-
- // Disable the chat auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.chat.enabled=false")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isEmpty());
-
- // The chat auto-configuration is enabled by default.
- this.contextRunner.run(context -> assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isNotEmpty());
-
- // Explicitly enable the chat auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.chat.enabled=true")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiChatModel.class)).isNotEmpty());
- }
-
- @Test
- void embeddingActivation() {
-
- // Disable the embedding auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.embedding.enabled=false")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isEmpty());
-
- // The embedding auto-configuration is enabled by default.
- this.contextRunner
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isNotEmpty());
-
- // Explicitly enable the embedding auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.embedding.enabled=true")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiEmbeddingModel.class)).isNotEmpty());
- }
-
- @Test
- void audioTranscriptionActivation() {
-
- // Disable the transcription auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.audio.transcription.enabled=false")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isEmpty());
-
- // The transcription auto-configuration is enabled by default.
- this.contextRunner
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isNotEmpty());
-
- // Explicitly enable the transcription auto-configuration.
- this.contextRunner.withPropertyValues("spring.ai.azure.openai.audio.transcription.enabled=true")
- .run(context -> assertThat(context.getBeansOfType(AzureOpenAiAudioTranscriptionModel.class)).isNotEmpty());
- }
-
- @Test
- void openAIClientBuilderCustomizer() {
- AtomicBoolean firstCustomizationApplied = new AtomicBoolean(false);
- AtomicBoolean secondCustomizationApplied = new AtomicBoolean(false);
- this.contextRunner
- .withBean("first", AzureOpenAIClientBuilderCustomizer.class,
- () -> clientBuilder -> firstCustomizationApplied.set(true))
- .withBean("second", AzureOpenAIClientBuilderCustomizer.class,
- () -> clientBuilder -> secondCustomizationApplied.set(true))
- .run(context -> {
- context.getBean(OpenAIClientBuilder.class);
- assertThat(firstCustomizationApplied.get()).isTrue();
- assertThat(secondCustomizationApplied.get()).isTrue();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationPropertyTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationPropertyTests.java
deleted file mode 100644
index df965f34e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiAutoConfigurationPropertyTests.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties;
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiConnectionProperties;
-import org.springframework.ai.model.azure.openai.autoconfigure.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 AzureOpenAiAutoConfigurationPropertyTests {
-
- @Test
- public void embeddingPropertiesTest() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.azure.openai.api-key=TEST_API_KEY",
- "spring.ai.azure.openai.endpoint=TEST_ENDPOINT",
- "spring.ai.azure.openai.embedding.options.deployment-name=MODEL_XYZ")
- .withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(AzureOpenAiEmbeddingProperties.class);
- var connectionProperties = context.getBean(AzureOpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("TEST_API_KEY");
- assertThat(connectionProperties.getEndpoint()).isEqualTo("TEST_ENDPOINT");
-
- assertThat(chatProperties.getOptions().getDeploymentName()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void chatPropertiesTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.api-key=API_KEY",
- "spring.ai.azure.openai.endpoint=ENDPOINT",
-
- "spring.ai.azure.openai.chat.options.deployment-name=MODEL_XYZ",
- "spring.ai.azure.openai.chat.options.frequencyPenalty=-1.5",
- "spring.ai.azure.openai.chat.options.logitBias.myTokenId=-5",
- "spring.ai.azure.openai.chat.options.maxTokens=123",
- "spring.ai.azure.openai.chat.options.n=10",
- "spring.ai.azure.openai.chat.options.presencePenalty=0",
- "spring.ai.azure.openai.chat.options.stop=boza,koza",
- "spring.ai.azure.openai.chat.options.temperature=0.55",
- "spring.ai.azure.openai.chat.options.topP=0.56",
- "spring.ai.azure.openai.chat.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(AzureOpenAiChatProperties.class);
- var connectionProperties = context.getBean(AzureOpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(AzureOpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getEndpoint()).isEqualTo("ENDPOINT");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getDeploymentName()).isEqualTo("text-embedding-ada-002");
-
- assertThat(chatProperties.getOptions().getDeploymentName()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5);
- assertThat(chatProperties.getOptions().getLogitBias().get("myTokenId")).isEqualTo(-5);
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getN()).isEqualTo(10);
- assertThat(chatProperties.getOptions().getPresencePenalty()).isEqualTo(0);
- assertThat(chatProperties.getOptions().getStop()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
-
- assertThat(chatProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiDirectOpenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiDirectOpenAiAutoConfigurationIT.java
deleted file mode 100644
index 99c4ed05c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/AzureOpenAiDirectOpenAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure;
-
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
-import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.chat.prompt.SystemPromptTemplate;
-import org.springframework.ai.embedding.EmbeddingResponse;
-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 1.0.0
- */
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class AzureOpenAiDirectOpenAiAutoConfigurationIT {
-
- private static String CHAT_MODEL_NAME = "gpt-4o";
-
- private static String EMBEDDING_MODEL_NAME = "text-embedding-ada-002";
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.openai-api-key=" + System.getenv("OPENAI_API_KEY"),
-
- "spring.ai.azure.openai.chat.options.deployment-name=" + CHAT_MODEL_NAME,
- "spring.ai.azure.openai.chat.options.temperature=0.8",
- "spring.ai.azure.openai.chat.options.maxTokens=123",
- "spring.ai.azure.openai.embedding.options.deployment-name=" + EMBEDDING_MODEL_NAME
- // @formatter:on
- ).withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class));
-
- private final Message systemMessage = new SystemPromptTemplate("""
- You are a helpful AI assistant. Your name is {name}.
- You are an AI assistant that helps people find information.
- Your name is {name}
- You should reply to the user's request with your name and also in the style of a {voice}.
- """).createMessage(Map.of("name", "Bob", "voice", "pirate"));
-
- private final UserMessage userMessage = new UserMessage(
- "Tell me about 3 famous pirates from the Golden Age of Piracy and why they did.");
-
- @Test
- public void chatCompletion() {
- this.contextRunner.run(context -> {
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
- ChatResponse response = chatModel.call(new Prompt(List.of(this.userMessage, this.systemMessage)));
- assertThat(response.getResult().getOutput().getText()).contains("Blackbeard");
- });
- }
-
- @Test
- public void chatCompletionStreaming() {
- this.contextRunner.run(context -> {
-
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- Flux response = chatModel.stream(new Prompt(List.of(this.userMessage, this.systemMessage)));
-
- List responses = response.collectList().block();
- assertThat(responses.size()).isGreaterThan(10);
-
- String stitchedResponseContent = responses.stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(stitchedResponseContent).contains("Blackbeard");
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- AzureOpenAiEmbeddingModel embeddingModel = context.getBean(AzureOpenAiEmbeddingModel.class);
-
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1536);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/DeploymentNameUtil.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/DeploymentNameUtil.java
deleted file mode 100644
index 96655413b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/DeploymentNameUtil.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure.tool;
-
-import org.springframework.util.StringUtils;
-
-public final class DeploymentNameUtil {
-
- private DeploymentNameUtil() {
-
- }
-
- public static String getDeploymentName() {
- String deploymentName = System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME");
- if (StringUtils.hasText(deploymentName)) {
- return deploymentName;
- }
- else {
- return "gpt-4o";
- }
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java
deleted file mode 100644
index 5f560d174..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.azure.openai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.function.Function;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
-import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
-class FunctionCallWithFunctionBeanIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithFunctionBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.api-key=" + System.getenv("AZURE_OPENAI_API_KEY"),
- "spring.ai.azure.openai.endpoint=" + System.getenv("AZURE_OPENAI_ENDPOINT"))
- // @formatter:onn
- .withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.azure.openai.chat.options..deployment-name=" + DeploymentNameUtil.getDeploymentName())
- .run(context -> {
-
- ChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Paris and in Tokyo? Use Multi-turn function calling.");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- AzureOpenAiChatOptions.builder().function("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- response = chatModel.call(new Prompt(List.of(userMessage),
- AzureOpenAiChatOptions.builder().function("weatherFunction3").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.azure.openai.chat.options..deployment-name=" + DeploymentNameUtil.getDeploymentName())
- .run(context -> {
-
- ChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Paris and in Tokyo? Use Multi-turn function calling.");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- ToolCallingChatOptions.builder().toolNames("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get the weather in location")
- public Function weatherFunction() {
- return new MockWeatherService();
- }
-
- // Relies on the Request's JsonClassDescription annotation to provide the
- // function description.
- @Bean
- public Function weatherFunction3() {
- MockWeatherService weatherService = new MockWeatherService();
- return (weatherService::apply);
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionWrapperIT.java
deleted file mode 100644
index b809da5dd..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionWrapperIT.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure.tool;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
-import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-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 static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
-public class FunctionCallWithFunctionWrapperIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithFunctionWrapperIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.api-key=" + System.getenv("AZURE_OPENAI_API_KEY"),
- "spring.ai.azure.openai.endpoint=" + System.getenv("AZURE_OPENAI_ENDPOINT"))
- // @formatter:onn
- .withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.azure.openai.chat.options.deployment-name=" + DeploymentNameUtil.getDeploymentName())
- .run(context -> {
-
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Paris and in Tokyo?");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- AzureOpenAiChatOptions.builder().function("WeatherInfo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsAnyOf("30", "10", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ToolCallback weatherFunctionInfo() {
-
- return FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
- .description("Get the current weather in a given location")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java
deleted file mode 100644
index 90e5d7a9b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithPromptFunctionIT.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure.tool;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiAutoConfiguration;
-import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
-import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "AZURE_OPENAI_ENDPOINT", matches = ".+")
-public class FunctionCallWithPromptFunctionIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithPromptFunctionIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.azure.openai.api-key=" + System.getenv("AZURE_OPENAI_API_KEY"),
- "spring.ai.azure.openai.endpoint=" + System.getenv("AZURE_OPENAI_ENDPOINT"))
- // @formatter:onn
- .withConfiguration(AutoConfigurations.of(AzureOpenAiAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.azure.openai.chat.options.deployment-name=" + DeploymentNameUtil.getDeploymentName())
- .run(context -> {
-
- AzureOpenAiChatModel chatModel = context.getBean(AzureOpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, in Paris and in Tokyo? Use Multi-turn function calling.");
-
- var promptOptions = AzureOpenAiChatOptions.builder()
- .functionCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/MockWeatherService.java
deleted file mode 100644
index 20c1899ef..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/MockWeatherService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.model.azure.openai.autoconfigure.tool;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Christian Tzolov
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 0;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Weather API request")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
-
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfigurationIT.java
deleted file mode 100644
index af51a1e93..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockAwsConnectionConfigurationIT.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-import software.amazon.awssdk.auth.credentials.AwsCredentials;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-import software.amazon.awssdk.regions.Region;
-import software.amazon.awssdk.regions.providers.AwsRegionProvider;
-
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Import;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Wei Jiang
- * @author Mark Pollack
- * @since 1.0.0
- */
-@RequiresAwsCredentials
-public class BedrockAwsConnectionConfigurationIT {
-
- @Test
- public void autoConfigureAWSCredentialAndRegionProvider() {
- BedrockTestUtils.getContextRunner()
- .withConfiguration(AutoConfigurations.of(TestAutoConfiguration.class))
- .run(context -> {
- var awsCredentialsProvider = context.getBean(AwsCredentialsProvider.class);
- var awsRegionProvider = context.getBean(AwsRegionProvider.class);
-
- assertThat(awsCredentialsProvider).isNotNull();
- assertThat(awsRegionProvider).isNotNull();
-
- var credentials = awsCredentialsProvider.resolveCredentials();
- assertThat(credentials).isNotNull();
- assertThat(credentials.accessKeyId()).isEqualTo(System.getenv("AWS_ACCESS_KEY_ID"));
- assertThat(credentials.secretAccessKey()).isEqualTo(System.getenv("AWS_SECRET_ACCESS_KEY"));
-
- assertThat(awsRegionProvider.getRegion()).isEqualTo(Region.US_EAST_1);
- });
- }
-
- @Test
- public void autoConfigureWithCustomAWSCredentialAndRegionProvider() {
- BedrockTestUtils.getContextRunner()
- .withConfiguration(AutoConfigurations.of(TestAutoConfiguration.class,
- CustomAwsCredentialsProviderAndAwsRegionProviderAutoConfiguration.class))
- .run(context -> {
- var awsCredentialsProvider = context.getBean(AwsCredentialsProvider.class);
- var awsRegionProvider = context.getBean(AwsRegionProvider.class);
-
- assertThat(awsCredentialsProvider).isNotNull();
- assertThat(awsRegionProvider).isNotNull();
-
- var credentials = awsCredentialsProvider.resolveCredentials();
- assertThat(credentials).isNotNull();
- assertThat(credentials.accessKeyId()).isEqualTo("CUSTOM_ACCESS_KEY");
- assertThat(credentials.secretAccessKey()).isEqualTo("CUSTOM_SECRET_ACCESS_KEY");
-
- assertThat(awsRegionProvider.getRegion()).isEqualTo(Region.AWS_GLOBAL);
- });
- }
-
- @EnableConfigurationProperties({ BedrockAwsConnectionProperties.class })
- @Import(BedrockAwsConnectionConfiguration.class)
- static class TestAutoConfiguration {
-
- }
-
- @AutoConfiguration
- static class CustomAwsCredentialsProviderAndAwsRegionProviderAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public AwsCredentialsProvider credentialsProvider() {
- return new AwsCredentialsProvider() {
-
- @Override
- public AwsCredentials resolveCredentials() {
- return new AwsCredentials() {
-
- @Override
- public String accessKeyId() {
- return "CUSTOM_ACCESS_KEY";
- }
-
- @Override
- public String secretAccessKey() {
- return "CUSTOM_SECRET_ACCESS_KEY";
- }
-
- };
- }
-
- };
- }
-
- @Bean
- @ConditionalOnMissingBean
- public AwsRegionProvider regionProvider() {
- return new AwsRegionProvider() {
-
- @Override
- public Region getRegion() {
- return Region.AWS_GLOBAL;
- }
-
- };
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockTestUtils.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockTestUtils.java
deleted file mode 100644
index 88b12b6c3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/BedrockTestUtils.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import software.amazon.awssdk.regions.Region;
-
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-public final class BedrockTestUtils {
-
- private BedrockTestUtils() {
- } // Prevent instantiation
-
- public static ApplicationContextRunner getContextRunner() {
- return new ApplicationContextRunner()
- .withPropertyValues("spring.ai.bedrock.aws.access-key=" + System.getenv("AWS_ACCESS_KEY_ID"),
- "spring.ai.bedrock.aws.secret-key=" + System.getenv("AWS_SECRET_ACCESS_KEY"),
- "spring.ai.bedrock.aws.session-token=" + System.getenv("AWS_SESSION_TOKEN"),
- "spring.ai.bedrock.aws.region=" + Region.US_EAST_1.id())
- .withUserConfiguration(Config.class);
- }
-
- public static ApplicationContextRunner getContextRunnerWithUserConfiguration() {
- return new ApplicationContextRunner().withUserConfiguration(Config.class);
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ObjectMapper objectMapper() {
- return new ObjectMapper();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/RequiresAwsCredentials.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/RequiresAwsCredentials.java
deleted file mode 100644
index 00f5e633b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/RequiresAwsCredentials.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-
-@Target({ ElementType.TYPE, ElementType.METHOD })
-@Retention(RetentionPolicy.RUNTIME)
-@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".*")
-@EnabledIfEnvironmentVariable(named = "AWS_SECRET_ACCESS_KEY", matches = ".*")
-@EnabledIfEnvironmentVariable(named = "AWS_SESSION_TOKEN", matches = ".*")
-public @interface RequiresAwsCredentials {
-
- // You can add custom properties here if needed
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfigurationIT.java
deleted file mode 100644
index f612425cf..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/cohere/BedrockCohereEmbeddingAutoConfigurationIT.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.cohere;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import software.amazon.awssdk.regions.Region;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockTestUtils;
-import org.springframework.ai.model.bedrock.autoconfigure.RequiresAwsCredentials;
-import org.springframework.ai.bedrock.cohere.BedrockCohereEmbeddingModel;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingModel;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest;
-import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest.InputType;
-import org.springframework.ai.embedding.EmbeddingResponse;
-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
- * @author Mark Pollack
- * @since 1.0.0
- */
-@RequiresAwsCredentials
-public class BedrockCohereEmbeddingAutoConfigurationIT {
-
- private final ApplicationContextRunner contextRunner = BedrockTestUtils.getContextRunner()
- .withPropertyValues("spring.ai.bedrock.cohere.embedding.enabled=true",
- "spring.ai.bedrock.cohere.embedding.model=" + CohereEmbeddingModel.COHERE_EMBED_MULTILINGUAL_V3.id(),
- "spring.ai.bedrock.cohere.embedding.options.inputType=SEARCH_DOCUMENT",
- "spring.ai.bedrock.cohere.embedding.options.truncate=NONE")
- .withConfiguration(AutoConfigurations.of(BedrockCohereEmbeddingAutoConfiguration.class));
-
- @Test
- public void singleEmbedding() {
- this.contextRunner.run(context -> {
- BedrockCohereEmbeddingModel embeddingModel = context.getBean(BedrockCohereEmbeddingModel.class);
- assertThat(embeddingModel).isNotNull();
- EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
- assertThat(embeddingResponse.getResults()).hasSize(1);
- assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
- assertThat(embeddingModel.dimensions()).isEqualTo(1024);
- });
- }
-
- @Test
- public void batchEmbedding() {
- this.contextRunner.run(context -> {
-
- BedrockCohereEmbeddingModel embeddingModel = context.getBean(BedrockCohereEmbeddingModel.class);
-
- assertThat(embeddingModel).isNotNull();
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1024);
-
- });
- }
-
- @Test
- public void propertiesTest() {
-
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.cohere.embedding.enabled=true",
- "spring.ai.bedrock.aws.access-key=ACCESS_KEY", "spring.ai.bedrock.aws.secret-key=SECRET_KEY",
- "spring.ai.bedrock.aws.region=" + Region.US_EAST_1.id(),
- "spring.ai.bedrock.cohere.embedding.model=MODEL_XYZ",
- "spring.ai.bedrock.cohere.embedding.options.inputType=CLASSIFICATION",
- "spring.ai.bedrock.cohere.embedding.options.truncate=START")
- .withConfiguration(AutoConfigurations.of(BedrockCohereEmbeddingAutoConfiguration.class))
- .run(context -> {
- var properties = context.getBean(BedrockCohereEmbeddingProperties.class);
- var awsProperties = context.getBean(BedrockAwsConnectionProperties.class);
-
- assertThat(properties.isEnabled()).isTrue();
- assertThat(awsProperties.getRegion()).isEqualTo(Region.US_EAST_1.id());
- assertThat(properties.getModel()).isEqualTo("MODEL_XYZ");
-
- assertThat(properties.getOptions().getInputType()).isEqualTo(InputType.CLASSIFICATION);
- assertThat(properties.getOptions().getTruncate()).isEqualTo(CohereEmbeddingRequest.Truncate.START);
-
- assertThat(awsProperties.getAccessKey()).isEqualTo("ACCESS_KEY");
- assertThat(awsProperties.getSecretKey()).isEqualTo("SECRET_KEY");
- });
- }
-
- @Test
- public void embeddingDisabled() {
-
- // It is disabled by default
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withConfiguration(AutoConfigurations.of(BedrockCohereEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingModel.class)).isEmpty();
- });
-
- // Explicitly enable the embedding auto-configuration.
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.cohere.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(BedrockCohereEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingModel.class)).isNotEmpty();
- });
-
- // Explicitly disable the embedding auto-configuration.
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.cohere.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(BedrockCohereEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BedrockCohereEmbeddingModel.class)).isEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatAutoConfigurationIT.java
deleted file mode 100644
index f89cda503..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatAutoConfigurationIT.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.converse;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockTestUtils;
-import org.springframework.ai.model.bedrock.autoconfigure.RequiresAwsCredentials;
-import org.springframework.ai.bedrock.converse.BedrockProxyChatModel;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@RequiresAwsCredentials
-public class BedrockConverseProxyChatAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(BedrockConverseProxyChatAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = BedrockTestUtils.getContextRunner()
- .withPropertyValues(
- "spring.ai.bedrock.converse.chat.options.model=" + "anthropic.claude-3-5-sonnet-20240620-v1:0",
- "spring.ai.bedrock.converse.chat.options.temperature=0.5")
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class));
-
- @Test
- void call() {
- this.contextRunner.run(context -> {
- BedrockProxyChatModel chatModel = context.getBean(BedrockProxyChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void stream() {
- this.contextRunner.run(context -> {
- BedrockProxyChatModel chatModel = context.getBean(BedrockProxyChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
-
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatPropertiesTests.java
deleted file mode 100644
index b71900197..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/BedrockConverseProxyChatPropertiesTests.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.converse;
-
-import org.junit.jupiter.api.Test;
-
-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
- *
- * Unit Tests for {@link BedrockConverseProxyChatProperties}.
- */
-public class BedrockConverseProxyChatPropertiesTests {
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.bedrock.converse.chat.options.model=MODEL_XYZ",
-
- "spring.ai.bedrock.converse.chat.options.max-tokens=123",
- "spring.ai.bedrock.converse.chat.options.metadata.user-id=MyUserId",
- "spring.ai.bedrock.converse.chat.options.stop_sequences=boza,koza",
-
- "spring.ai.bedrock.converse.chat.options.temperature=0.55",
- "spring.ai.bedrock.converse.chat.options.top-p=0.56",
- "spring.ai.bedrock.converse.chat.options.top-k=100"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(BedrockConverseProxyChatProperties.class);
-
- assertThat(chatProperties.isEnabled()).isTrue();
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getStopSequences()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
- assertThat(chatProperties.getOptions().getTopK()).isEqualTo(100);
-
- });
- }
-
- @Test
- public void chatCompletionDisabled() {
-
- // It is enabled by default
- new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class))
- .run(context -> assertThat(context.getBeansOfType(BedrockConverseProxyChatProperties.class)).isNotEmpty());
-
- // Explicitly enable the chat auto-configuration.
- new ApplicationContextRunner().withPropertyValues("spring.ai.bedrock.converse.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class))
- .run(context -> assertThat(context.getBeansOfType(BedrockConverseProxyChatProperties.class)).isNotEmpty());
-
- // Explicitly disable the chat auto-configuration.
- new ApplicationContextRunner().withPropertyValues("spring.ai.bedrock.converse.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class))
- .run(context -> assertThat(context.getBeansOfType(BedrockConverseProxyChatProperties.class)).isEmpty());
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/FunctionCallWithPromptFunctionIT.java
deleted file mode 100644
index 95c595370..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/FunctionCallWithPromptFunctionIT.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.converse.tool;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockTestUtils;
-import org.springframework.ai.model.bedrock.autoconfigure.RequiresAwsCredentials;
-import org.springframework.ai.model.bedrock.autoconfigure.converse.BedrockConverseProxyChatAutoConfiguration;
-import org.springframework.ai.bedrock.converse.BedrockProxyChatModel;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@RequiresAwsCredentials
-public class FunctionCallWithPromptFunctionIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallWithPromptFunctionIT.class);
-
- private final ApplicationContextRunner contextRunner = BedrockTestUtils.getContextRunner()
- .withConfiguration(AutoConfigurations.of(BedrockConverseProxyChatAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.bedrock.converse.chat.options.model=" + "anthropic.claude-3-5-sonnet-20240620-v1:0")
- .run(context -> {
-
- BedrockProxyChatModel chatModel = context.getBean(BedrockProxyChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, in Paris and in Tokyo? Return the temperature in Celsius.");
-
- var promptOptions = ToolCallingChatOptions.builder()
- .toolCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location. Return temperature in 36°F or 36°C format.")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/MockWeatherService.java
deleted file mode 100644
index 29081ec2f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/converse/tool/MockWeatherService.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.converse.tool;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Christian Tzolov
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 0;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Weather API request")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfigurationIT.java
deleted file mode 100644
index 107bb2a6d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/bedrock/autoconfigure/titan/BedrockTitanEmbeddingAutoConfigurationIT.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.model.bedrock.autoconfigure.titan;
-
-import java.util.Base64;
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import software.amazon.awssdk.regions.Region;
-
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockAwsConnectionProperties;
-import org.springframework.ai.model.bedrock.autoconfigure.BedrockTestUtils;
-import org.springframework.ai.model.bedrock.autoconfigure.RequiresAwsCredentials;
-import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingModel;
-import org.springframework.ai.bedrock.titan.BedrockTitanEmbeddingModel.InputType;
-import org.springframework.ai.bedrock.titan.api.TitanEmbeddingBedrockApi.TitanEmbeddingModel;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.core.io.DefaultResourceLoader;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- * @author Mark Pollack
- * @since 1.0.0
- */
-@RequiresAwsCredentials
-public class BedrockTitanEmbeddingAutoConfigurationIT {
-
- private final ApplicationContextRunner contextRunner = BedrockTestUtils.getContextRunner()
- .withPropertyValues("spring.ai.bedrock.titan.embedding.enabled=true",
- "spring.ai.bedrock.aws.access-key=" + System.getenv("AWS_ACCESS_KEY_ID"),
- "spring.ai.bedrock.aws.secret-key=" + System.getenv("AWS_SECRET_ACCESS_KEY"),
- "spring.ai.bedrock.aws.region=" + Region.US_EAST_1.id(),
- "spring.ai.bedrock.titan.embedding.model=" + TitanEmbeddingModel.TITAN_EMBED_IMAGE_V1.id())
- .withConfiguration(AutoConfigurations.of(BedrockTitanEmbeddingAutoConfiguration.class));
-
- @Test
- public void singleTextEmbedding() {
- this.contextRunner.withPropertyValues("spring.ai.bedrock.titan.embedding.inputType=TEXT").run(context -> {
- BedrockTitanEmbeddingModel embeddingModel = context.getBean(BedrockTitanEmbeddingModel.class);
- assertThat(embeddingModel).isNotNull();
- EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
- assertThat(embeddingResponse.getResults()).hasSize(1);
- assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
- assertThat(embeddingModel.dimensions()).isEqualTo(1024);
- });
- }
-
- @Test
- public void singleImageEmbedding() {
- this.contextRunner.withPropertyValues("spring.ai.bedrock.titan.embedding.inputType=IMAGE").run(context -> {
- BedrockTitanEmbeddingModel embeddingModel = context.getBean(BedrockTitanEmbeddingModel.class);
- assertThat(embeddingModel).isNotNull();
-
- byte[] image = new DefaultResourceLoader().getResource("classpath:/spring_framework.png")
- .getContentAsByteArray();
-
- var base64Image = Base64.getEncoder().encodeToString(image);
-
- EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of(base64Image));
-
- assertThat(embeddingResponse.getResults()).hasSize(1);
- assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
- assertThat(embeddingModel.dimensions()).isEqualTo(1024);
- });
- }
-
- @Test
- public void propertiesTest() {
-
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.titan.embedding.enabled=true",
- "spring.ai.bedrock.aws.access-key=ACCESS_KEY", "spring.ai.bedrock.aws.secret-key=SECRET_KEY",
- "spring.ai.bedrock.aws.region=" + Region.US_EAST_1.id(),
- "spring.ai.bedrock.titan.embedding.model=MODEL_XYZ",
- "spring.ai.bedrock.titan.embedding.inputType=TEXT")
- .withConfiguration(AutoConfigurations.of(BedrockTitanEmbeddingAutoConfiguration.class))
- .run(context -> {
- var properties = context.getBean(BedrockTitanEmbeddingProperties.class);
- var awsProperties = context.getBean(BedrockAwsConnectionProperties.class);
-
- assertThat(properties.isEnabled()).isTrue();
- assertThat(awsProperties.getRegion()).isEqualTo(Region.US_EAST_1.id());
- assertThat(properties.getModel()).isEqualTo("MODEL_XYZ");
-
- assertThat(properties.getInputType()).isEqualTo(InputType.TEXT);
-
- assertThat(awsProperties.getAccessKey()).isEqualTo("ACCESS_KEY");
- assertThat(awsProperties.getSecretKey()).isEqualTo("SECRET_KEY");
- });
- }
-
- @Test
- public void embeddingDisabled() {
-
- // It is disabled by default
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withConfiguration(AutoConfigurations.of(BedrockTitanEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingModel.class)).isEmpty();
- });
-
- // Explicitly enable the embedding auto-configuration.
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.titan.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(BedrockTitanEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingModel.class)).isNotEmpty();
- });
-
- // Explicitly disable the embedding auto-configuration.
- BedrockTestUtils.getContextRunnerWithUserConfiguration()
- .withPropertyValues("spring.ai.bedrock.titan.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(BedrockTitanEmbeddingAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BedrockTitanEmbeddingModel.class)).isEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfigurationIT.java
deleted file mode 100644
index 5906eb3b9..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientAutoConfigurationIT.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.model.chat.client.autoconfigure;
-
-import java.util.List;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.client.ChatClientCustomizer;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- */
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class ChatClientAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(ChatClientAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"),
- "spring.ai.openai.chat.options.model=gpt-4o")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, OpenAiAutoConfiguration.class, ChatClientAutoConfiguration.class));
-
- @Test
- void implicitlyEnabled() {
- this.contextRunner.run(context -> assertThat(context.getBeansOfType(ChatClient.Builder.class)).isNotEmpty());
- }
-
- @Test
- void explicitlyEnabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.client.enabled=true")
- .run(context -> assertThat(context.getBeansOfType(ChatClient.Builder.class)).isNotEmpty());
- }
-
- @Test
- void explicitlyDisabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.client.enabled=false")
- .run(context -> assertThat(context.getBeansOfType(ChatClient.Builder.class)).isEmpty());
- }
-
- @Test
- void generate() {
- this.contextRunner.run(context -> {
- ChatClient.Builder builder = context.getBean(ChatClient.Builder.class);
-
- assertThat(builder).isNotNull();
-
- ChatClient chatClient = builder.build();
-
- String response = chatClient.prompt().user("Hello").call().content();
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void testChatClientCustomizers() {
- this.contextRunner.withUserConfiguration(Config.class).run(context -> {
-
- ChatClient.Builder builder = context.getBean(ChatClient.Builder.class);
-
- ChatClient chatClient = builder.build();
-
- assertThat(chatClient).isNotNull();
-
- ActorsFilms actorsFilms = chatClient.prompt()
- .user(u -> u.param("actor", "Tom Hanks"))
- .call()
- .entity(ActorsFilms.class);
-
- logger.info("" + actorsFilms);
- assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
- assertThat(actorsFilms.movies()).hasSize(5);
- });
- }
-
- record ActorsFilms(String actor, List movies) {
-
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ChatClientCustomizer chatClientCustomizer() {
- return b -> b.defaultSystem("You are a movie expert.")
- .defaultUser("Generate the filmography of 5 movies for {actor}.");
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientObservationAutoConfigurationTests.java
deleted file mode 100644
index 7fd0ee9f8..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/client/autoconfigure/ChatClientObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.model.chat.client.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.chat.client.observation.ChatClientInputContentObservationFilter;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ChatClientAutoConfiguration} observability support.
- *
- * @author Christian Tzolov
- */
-class ChatClientObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(ChatClientAutoConfiguration.class));
-
- @Test
- void inputContentFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatClientInputContentObservationFilter.class));
- }
-
- @Test
- void inputContentFilterEnabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.client.observations.include-input=true")
- .run(context -> assertThat(context).hasSingleBean(ChatClientInputContentObservationFilter.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfigurationIT.java
deleted file mode 100644
index a41904290..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryAutoConfigurationIT.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.model.chat.memory.autoconfigure.cassandra;
-
-import java.time.Duration;
-import java.util.List;
-
-import com.datastax.driver.core.utils.UUIDs;
-import org.junit.jupiter.api.Test;
-import org.testcontainers.containers.CassandraContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
-
-import org.springframework.ai.chat.memory.cassandra.CassandraChatMemory;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.MessageType;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Mick Semb Wever
- * @author Jihoon Kim
- * @since 1.0.0
- */
-@Testcontainers
-class CassandraChatMemoryAutoConfigurationIT {
-
- static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cassandra");
-
- @Container
- static CassandraContainer cassandraContainer = new CassandraContainer(DEFAULT_IMAGE_NAME.withTag("5.0"));
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(
- AutoConfigurations.of(CassandraChatMemoryAutoConfiguration.class, CassandraAutoConfiguration.class))
- .withPropertyValues("spring.ai.chat.memory.cassandra.keyspace=test_autoconfigure");
-
- @Test
- void addAndGet() {
- this.contextRunner.withPropertyValues("spring.cassandra.contactPoints=" + getContactPointHost())
- .withPropertyValues("spring.cassandra.port=" + getContactPointPort())
- .withPropertyValues("spring.cassandra.localDatacenter=" + cassandraContainer.getLocalDatacenter())
- .withPropertyValues("spring.ai.chat.memory.cassandra.time-to-live=" + getTimeToLive())
- .run(context -> {
- CassandraChatMemory memory = context.getBean(CassandraChatMemory.class);
-
- String sessionId = UUIDs.timeBased().toString();
- assertThat(memory.get(sessionId, Integer.MAX_VALUE)).isEmpty();
-
- memory.add(sessionId, new UserMessage("test question"));
-
- assertThat(memory.get(sessionId, Integer.MAX_VALUE)).hasSize(1);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(0).getMessageType())
- .isEqualTo(MessageType.USER);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(0).getText()).isEqualTo("test question");
-
- memory.clear(sessionId);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE)).isEmpty();
-
- memory.add(sessionId, List.of(new UserMessage("test question"), new AssistantMessage("test answer")));
-
- assertThat(memory.get(sessionId, Integer.MAX_VALUE)).hasSize(2);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(1).getMessageType())
- .isEqualTo(MessageType.USER);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(1).getText()).isEqualTo("test question");
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(0).getMessageType())
- .isEqualTo(MessageType.ASSISTANT);
- assertThat(memory.get(sessionId, Integer.MAX_VALUE).get(0).getText()).isEqualTo("test answer");
-
- CassandraChatMemoryProperties properties = context.getBean(CassandraChatMemoryProperties.class);
- assertThat(properties.getTimeToLive()).isEqualTo(getTimeToLive());
- });
- }
-
- @Test
- void compareTimeToLive_ISO8601Format() {
- this.contextRunner.withPropertyValues("spring.cassandra.contactPoints=" + getContactPointHost())
- .withPropertyValues("spring.cassandra.port=" + getContactPointPort())
- .withPropertyValues("spring.cassandra.localDatacenter=" + cassandraContainer.getLocalDatacenter())
- .withPropertyValues("spring.ai.chat.memory.cassandra.time-to-live=" + getTimeToLiveString())
- .run(context -> {
- CassandraChatMemoryProperties properties = context.getBean(CassandraChatMemoryProperties.class);
- assertThat(properties.getTimeToLive()).isEqualTo(Duration.parse(getTimeToLiveString()));
- });
- }
-
- private String getContactPointHost() {
- return cassandraContainer.getContactPoint().getHostString();
- }
-
- private String getContactPointPort() {
- return String.valueOf(cassandraContainer.getContactPoint().getPort());
- }
-
- private Duration getTimeToLive() {
- return Duration.ofSeconds(12000);
- }
-
- private String getTimeToLiveString() {
- return "PT1M";
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryPropertiesTest.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryPropertiesTest.java
deleted file mode 100644
index dab9eb806..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/memory/autoconfigure/cassandra/CassandraChatMemoryPropertiesTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.chat.memory.autoconfigure.cassandra;
-
-import java.time.Duration;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.chat.memory.cassandra.CassandraChatMemoryConfig;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Mick Semb Wever
- * @author Jihoon Kim
- * @since 1.0.0
- */
-class CassandraChatMemoryPropertiesTest {
-
- @Test
- void defaultValues() {
- var props = new CassandraChatMemoryProperties();
- assertThat(props.getKeyspace()).isEqualTo(CassandraChatMemoryConfig.DEFAULT_KEYSPACE_NAME);
- assertThat(props.getTable()).isEqualTo(CassandraChatMemoryConfig.DEFAULT_TABLE_NAME);
- assertThat(props.getAssistantColumn()).isEqualTo(CassandraChatMemoryConfig.DEFAULT_ASSISTANT_COLUMN_NAME);
- assertThat(props.getUserColumn()).isEqualTo(CassandraChatMemoryConfig.DEFAULT_USER_COLUMN_NAME);
- assertThat(props.getTimeToLive()).isNull();
- assertThat(props.isInitializeSchema()).isTrue();
- }
-
- @Test
- void customValues() {
- var props = new CassandraChatMemoryProperties();
- props.setKeyspace("my_keyspace");
- props.setTable("my_table");
- props.setAssistantColumn("my_assistant_column");
- props.setUserColumn("my_user_column");
- props.setTimeToLive(Duration.ofDays(1));
- props.setInitializeSchema(false);
-
- assertThat(props.getKeyspace()).isEqualTo("my_keyspace");
- assertThat(props.getTable()).isEqualTo("my_table");
- assertThat(props.getAssistantColumn()).isEqualTo("my_assistant_column");
- assertThat(props.getUserColumn()).isEqualTo("my_user_column");
- assertThat(props.getTimeToLive()).isEqualTo(Duration.ofDays(1));
- assertThat(props.isInitializeSchema()).isFalse();
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfigurationTests.java
deleted file mode 100644
index 3ac3f12ca..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/chat/observation/autoconfigure/ChatObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.model.chat.observation.autoconfigure;
-
-import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
-import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import io.opentelemetry.api.OpenTelemetry;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelCompletionObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelMeterObservationHandler;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationFilter;
-import org.springframework.ai.chat.observation.ChatModelPromptContentObservationHandler;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ChatObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class ChatObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(ChatObservationAutoConfiguration.class));
-
- @Test
- void meterObservationHandlerEnabled() {
- this.contextRunner.withBean(CompositeMeterRegistry.class)
- .run(context -> assertThat(context).hasSingleBean(ChatModelMeterObservationHandler.class));
- }
-
- @Test
- void meterObservationHandlerDisabled() {
- this.contextRunner.run(context -> assertThat(context).doesNotHaveBean(ChatModelMeterObservationHandler.class));
- }
-
- @Test
- void promptFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationFilter.class));
- }
-
- @Test
- void promptHandlerDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void promptHandlerEnabled() {
- this.contextRunner
- .withBean(OtelTracer.class, OpenTelemetry.noop().getTracer("test"), new OtelCurrentTraceContext(), null)
- .withPropertyValues("spring.ai.chat.observations.include-prompt=true")
- .run(context -> assertThat(context).hasSingleBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void promptHandlerDisabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.observations.include-prompt=true")
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelPromptContentObservationHandler.class));
- }
-
- @Test
- void completionFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationFilter.class));
- }
-
- @Test
- void completionHandlerDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationHandler.class));
- }
-
- @Test
- void completionHandlerEnabled() {
- this.contextRunner
- .withBean(OtelTracer.class, OpenTelemetry.noop().getTracer("test"), new OtelCurrentTraceContext(), null)
- .withPropertyValues("spring.ai.chat.observations.include-completion=true")
- .run(context -> assertThat(context).hasSingleBean(ChatModelCompletionObservationHandler.class));
- }
-
- @Test
- void completionHandlerDisabled() {
- this.contextRunner.withPropertyValues("spring.ai.chat.observations.include-completion=true")
- .run(context -> assertThat(context).doesNotHaveBean(ChatModelCompletionObservationHandler.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfigurationTests.java
deleted file mode 100644
index 10c9feb7c..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/embedding/observation/autoconfigure/EmbeddingObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.model.embedding.observation.autoconfigure;
-
-import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.embedding.observation.EmbeddingModelMeterObservationHandler;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link EmbeddingObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class EmbeddingObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(EmbeddingObservationAutoConfiguration.class));
-
- @Test
- void meterObservationHandlerEnabled() {
- this.contextRunner.withBean(CompositeMeterRegistry.class)
- .run(context -> assertThat(context).hasSingleBean(EmbeddingModelMeterObservationHandler.class));
- }
-
- @Test
- void meterObservationHandlerDisabled() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(EmbeddingModelMeterObservationHandler.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfigurationIT.java
deleted file mode 100644
index db991fe08..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/huggingface/autoconfigure/HuggingfaceChatAutoConfigurationIT.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.model.huggingface.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.huggingface.HuggingfaceChatModel;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "HUGGINGFACE_API_KEY", matches = ".+")
-@EnabledIfEnvironmentVariable(named = "HUGGINGFACE_CHAT_URL", matches = ".+")
-public class HuggingfaceChatAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(HuggingfaceChatAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.huggingface.chat.api-key=" + System.getenv("HUGGINGFACE_API_KEY"),
- "spring.ai.huggingface.chat.url=" + System.getenv("HUGGINGFACE_CHAT_URL"))
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(HuggingfaceChatAutoConfiguration.class));
-
- @Test
- void generate() {
- this.contextRunner.run(context -> {
- HuggingfaceChatModel chatModel = context.getBean(HuggingfaceChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Disabled("Until streaming support is added")
- @Test
- void generateStreaming() {
- this.contextRunner.run(context -> {
- HuggingfaceChatModel chatModel = context.getBean(HuggingfaceChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
-
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfigurationTests.java
deleted file mode 100644
index 23417b67b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/image/observation/autoconfigure/ImageObservationAutoConfigurationTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.model.image.observation.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.image.observation.ImageModelPromptContentObservationFilter;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ImageObservationAutoConfiguration}.
- *
- * @author Thomas Vitale
- */
-class ImageObservationAutoConfigurationTests {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(ImageObservationAutoConfiguration.class));
-
- @Test
- void promptFilterDefault() {
- this.contextRunner
- .run(context -> assertThat(context).doesNotHaveBean(ImageModelPromptContentObservationFilter.class));
- }
-
- @Test
- void promptFilterEnabled() {
- this.contextRunner.withPropertyValues("spring.ai.image.observations.include-prompt=true")
- .run(context -> assertThat(context).hasSingleBean(ImageModelPromptContentObservationFilter.class));
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java
deleted file mode 100644
index f86e76185..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.minimax.MiniMaxChatModel;
-import org.springframework.ai.minimax.MiniMaxChatOptions;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".*")
-public class FunctionCallbackInPromptIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.apiKey=" + System.getenv("MINIMAX_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- var promptOptions = MiniMaxChatOptions.builder()
- .functionCallbacks(List.of(FunctionCallback.builder()
- .function("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamingFunctionCallTest() {
-
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- var promptOptions = MiniMaxChatOptions.builder()
- .functionCallbacks(List.of(FunctionCallback.builder()
- .function("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- Flux response = chatModel.stream(new Prompt(List.of(userMessage), promptOptions));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java
deleted file mode 100644
index 6d511b709..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.minimax.MiniMaxChatModel;
-import org.springframework.ai.minimax.MiniMaxChatOptions;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".*")
-class FunctionCallbackWithPlainFunctionBeanIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackWithPlainFunctionBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.apiKey=" + System.getenv("MINIMAX_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- // FIXME: multiple function calls may stop prematurely due to model performance
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- ChatResponse response = chatModel.call(
- new Prompt(List.of(userMessage), MiniMaxChatOptions.builder().function("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.call(new Prompt(List.of(userMessage),
- MiniMaxChatOptions.builder().function("weatherFunctionTwo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- ToolCallingChatOptions toolOptions = ToolCallingChatOptions.builder().toolNames("weatherFunction").build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), toolOptions));
-
- logger.info("Response: {}", response);
- });
- }
-
- // FIXME: multiple function calls may stop prematurely due to model performance
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- Flux response = chatModel.stream(
- new Prompt(List.of(userMessage), MiniMaxChatOptions.builder().function("weatherFunction").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.stream(new Prompt(List.of(userMessage),
- MiniMaxChatOptions.builder().function("weatherFunctionTwo").build()));
-
- content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get the weather in location")
- public Function weatherFunction() {
- return new MockWeatherService();
- }
-
- // Relies on the Request's JsonClassDescription annotation to provide the
- // function description.
- @Bean
- public Function weatherFunctionTwo() {
- MockWeatherService weatherService = new MockWeatherService();
- return (weatherService::apply);
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java
deleted file mode 100644
index 2b82ba7d4..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxAutoConfigurationIT.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.minimax.MiniMaxChatModel;
-import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".*")
-public class MiniMaxAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(MiniMaxAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.apiKey=" + System.getenv("MINIMAX_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class));
-
- @Test
- void generate() {
- this.contextRunner.run(context -> {
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void generateStreaming() {
- this.contextRunner.run(context -> {
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText())
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- MiniMaxEmbeddingModel embeddingModel = context.getBean(MiniMaxEmbeddingModel.class);
-
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1536);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java
deleted file mode 100644
index 5bf102959..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.minimax.MiniMaxChatModel;
-import org.springframework.ai.minimax.MiniMaxChatOptions;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-@EnabledIfEnvironmentVariable(named = "MINIMAX_API_KEY", matches = ".*")
-public class MiniMaxFunctionCallbackIT {
-
- private final Logger logger = LoggerFactory.getLogger(MiniMaxFunctionCallbackIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.apiKey=" + System.getenv("MINIMAX_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), MiniMaxChatOptions.builder().function("WeatherInfo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.minimax.chat.options.model=abab6.5s-chat").run(context -> {
-
- MiniMaxChatModel chatModel = context.getBean(MiniMaxChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.");
-
- Flux response = chatModel
- .stream(new Prompt(List.of(userMessage), MiniMaxChatOptions.builder().function("WeatherInfo").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public FunctionCallback weatherFunctionInfo() {
-
- return FunctionCallback.builder()
- .function("WeatherInfo", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java
deleted file mode 100644
index b0a75f743..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxPropertiesTests.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.skyscreamer.jsonassert.JSONCompareMode;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.minimax.MiniMaxChatModel;
-import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
-import org.springframework.ai.minimax.api.MiniMaxApi;
-import org.springframework.ai.model.ModelOptionsUtils;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link MiniMaxConnectionProperties}, {@link MiniMaxChatProperties} and
- * {@link MiniMaxEmbeddingProperties}.
- *
- * @author Geng Rong
- */
-public class MiniMaxPropertiesTests {
-
- @Test
- public void chatProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.api-key=abc123",
- "spring.ai.minimax.chat.options.model=MODEL_XYZ",
- "spring.ai.minimax.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MiniMaxChatProperties.class);
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isNull();
- assertThat(chatProperties.getBaseUrl()).isNull();
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void chatOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.api-key=abc123",
- "spring.ai.minimax.chat.base-url=TEST_BASE_URL2",
- "spring.ai.minimax.chat.api-key=456",
- "spring.ai.minimax.chat.options.model=MODEL_XYZ",
- "spring.ai.minimax.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MiniMaxChatProperties.class);
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isEqualTo("456");
- assertThat(chatProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void embeddingProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.api-key=abc123",
- "spring.ai.minimax.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(MiniMaxEmbeddingProperties.class);
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isNull();
- assertThat(embeddingProperties.getBaseUrl()).isNull();
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void embeddingOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.api-key=abc123",
- "spring.ai.minimax.embedding.base-url=TEST_BASE_URL2",
- "spring.ai.minimax.embedding.api-key=456",
- "spring.ai.minimax.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(MiniMaxEmbeddingProperties.class);
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isEqualTo("456");
- assertThat(embeddingProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.api-key=API_KEY",
- "spring.ai.minimax.base-url=TEST_BASE_URL",
-
- "spring.ai.minimax.chat.options.model=MODEL_XYZ",
- "spring.ai.minimax.chat.options.frequencyPenalty=-1.5",
- "spring.ai.minimax.chat.options.logitBias.myTokenId=-5",
- "spring.ai.minimax.chat.options.maxTokens=123",
- "spring.ai.minimax.chat.options.n=10",
- "spring.ai.minimax.chat.options.presencePenalty=0",
- "spring.ai.minimax.chat.options.responseFormat.type=json",
- "spring.ai.minimax.chat.options.seed=66",
- "spring.ai.minimax.chat.options.stop=boza,koza",
- "spring.ai.minimax.chat.options.temperature=0.55",
- "spring.ai.minimax.chat.options.topP=0.56",
-
- // "spring.ai.minimax.chat.options.toolChoice.functionName=toolChoiceFunctionName",
- "spring.ai.minimax.chat.options.toolChoice=" + ModelOptionsUtils.toJsonString(MiniMaxApi.ChatCompletionRequest.ToolChoiceBuilder.function("toolChoiceFunctionName")),
-
- "spring.ai.minimax.chat.options.tools[0].function.name=myFunction1",
- "spring.ai.minimax.chat.options.tools[0].function.description=function description",
- "spring.ai.minimax.chat.options.tools[0].function.jsonSchema=" + """
- {
- "type": "object",
- "properties": {
- "location": {
- "type": "string",
- "description": "The city and state e.g. San Francisco, CA"
- },
- "lat": {
- "type": "number",
- "description": "The city latitude"
- },
- "lon": {
- "type": "number",
- "description": "The city longitude"
- },
- "unit": {
- "type": "string",
- "enum": ["c", "f"]
- }
- },
- "required": ["location", "lat", "lon", "unit"]
- }
- """
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MiniMaxChatProperties.class);
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
- var embeddingProperties = context.getBean(MiniMaxEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("embo-01");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5);
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getN()).isEqualTo(10);
- assertThat(chatProperties.getOptions().getPresencePenalty()).isEqualTo(0);
- assertThat(chatProperties.getOptions().getResponseFormat())
- .isEqualTo(new MiniMaxApi.ChatCompletionRequest.ResponseFormat("json"));
- assertThat(chatProperties.getOptions().getSeed()).isEqualTo(66);
- assertThat(chatProperties.getOptions().getStop()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
-
- JSONAssert.assertEquals("{\"type\":\"function\",\"function\":{\"name\":\"toolChoiceFunctionName\"}}",
- chatProperties.getOptions().getToolChoice(), JSONCompareMode.LENIENT);
-
- assertThat(chatProperties.getOptions().getTools()).hasSize(1);
- var tool = chatProperties.getOptions().getTools().get(0);
- assertThat(tool.getType()).isEqualTo(MiniMaxApi.FunctionTool.Type.FUNCTION);
- var function = tool.getFunction();
- assertThat(function.getName()).isEqualTo("myFunction1");
- assertThat(function.getDescription()).isEqualTo("function description");
- assertThat(function.getParameters()).isNotEmpty();
- });
- }
-
- @Test
- public void embeddingOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.minimax.api-key=API_KEY",
- "spring.ai.minimax.base-url=TEST_BASE_URL",
-
- "spring.ai.minimax.embedding.options.model=MODEL_XYZ",
- "spring.ai.minimax.embedding.options.encodingFormat=MyEncodingFormat"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- var connectionProperties = context.getBean(MiniMaxConnectionProperties.class);
- var embeddingProperties = context.getBean(MiniMaxEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- void embeddingActivation() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxEmbeddingModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxEmbeddingModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxEmbeddingModel.class)).isNotEmpty();
- });
- }
-
- @Test
- void chatActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxChatModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxChatModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.minimax.api-key=API_KEY", "spring.ai.minimax.base-url=TEST_BASE_URL",
- "spring.ai.minimax.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MiniMaxAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MiniMaxChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MiniMaxChatModel.class)).isNotEmpty();
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MockWeatherService.java
deleted file mode 100644
index 3c95cf669..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MockWeatherService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.model.minimax.autoconfigure;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Geng Rong
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 0;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Get the weather in location")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
- @JsonProperty(required = true, value = "lat") @JsonPropertyDescription("The city latitude") double lat,
- @JsonProperty(required = true, value = "lon") @JsonPropertyDescription("The city longitude") double lon,
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiAutoConfigurationIT.java
deleted file mode 100644
index b7bb85226..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.mistralai.MistralAiChatModel;
-import org.springframework.ai.mistralai.MistralAiEmbeddingModel;
-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.1
- */
-@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
-public class MistralAiAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(MistralAiAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.apiKey=" + System.getenv("MISTRAL_AI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(MistralAiAutoConfiguration.class));
-
- @Test
- void generate() {
- this.contextRunner.run(context -> {
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void generateStreaming() {
- this.contextRunner.run(context -> {
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText())
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- MistralAiEmbeddingModel embeddingModel = context.getBean(MistralAiEmbeddingModel.class);
-
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1024);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiPropertiesTests.java
deleted file mode 100644
index dd4eae9ab..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/MistralAiPropertiesTests.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link MistralAiCommonProperties}, {@link MistralAiEmbeddingProperties}.
- */
-public class MistralAiPropertiesTests {
-
- @Test
- public void embeddingProperties() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.base-url=TEST_BASE_URL", "spring.ai.mistralai.api-key=abc123",
- "spring.ai.mistralai.embedding.options.model=MODEL_XYZ")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MistralAiAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(MistralAiEmbeddingProperties.class);
- var connectionProperties = context.getBean(MistralAiCommonProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isNull();
- assertThat(embeddingProperties.getBaseUrl()).isEqualTo(MistralAiCommonProperties.DEFAULT_BASE_URL);
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues("spring.ai.mistralai.base-url=TEST_BASE_URL",
- "spring.ai.mistralai.chat.options.tools[0].function.name=myFunction1",
- "spring.ai.mistralai.chat.options.tools[0].function.description=function description",
- "spring.ai.mistralai.chat.options.tools[0].function.jsonSchema=" + """
- {
- "type": "object",
- "properties": {
- "location": {
- "type": "string",
- "description": "The city and state e.g. San Francisco, CA"
- },
- "lat": {
- "type": "number",
- "description": "The city latitude"
- },
- "lon": {
- "type": "number",
- "description": "The city longitude"
- },
- "unit": {
- "type": "string",
- "enum": ["c", "f"]
- }
- },
- "required": ["location", "lat", "lon", "unit"]
- }
- """,
-
- "spring.ai.mistralai.api-key=abc123", "spring.ai.mistralai.embedding.base-url=TEST_BASE_URL2",
- "spring.ai.mistralai.embedding.api-key=456", "spring.ai.mistralai.embedding.options.model=MODEL_XYZ")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MistralAiAutoConfiguration.class))
- .run(context -> {
-
- var chatProperties = context.getBean(MistralAiChatProperties.class);
-
- var tool = chatProperties.getOptions().getTools().get(0);
- assertThat(tool.getType()).isEqualTo(MistralAiApi.FunctionTool.Type.FUNCTION);
- var function = tool.getFunction();
- assertThat(function.getName()).isEqualTo("myFunction1");
- assertThat(function.getDescription()).isEqualTo("function description");
- assertThat(function.getParameters()).isNotEmpty();
- });
- }
-
- @Test
- public void embeddingOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues("spring.ai.mistralai.base-url=TEST_BASE_URL",
- "spring.ai.mistralai.api-key=abc123", "spring.ai.mistralai.embedding.base-url=TEST_BASE_URL2",
- "spring.ai.mistralai.embedding.api-key=456", "spring.ai.mistralai.embedding.options.model=MODEL_XYZ")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MistralAiAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(MistralAiEmbeddingProperties.class);
- var connectionProperties = context.getBean(MistralAiCommonProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isEqualTo("456");
- assertThat(embeddingProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void embeddingOptionsTest() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.api-key=API_KEY", "spring.ai.mistralai.base-url=TEST_BASE_URL",
-
- "spring.ai.mistralai.embedding.options.model=MODEL_XYZ",
- "spring.ai.mistralai.embedding.options.encodingFormat=MyEncodingFormat")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MistralAiAutoConfiguration.class))
- .run(context -> {
- var connectionProperties = context.getBean(MistralAiCommonProperties.class);
- var embeddingProperties = context.getBean(MistralAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(embeddingProperties.getOptions().getEncodingFormat()).isEqualTo("MyEncodingFormat");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanIT.java
deleted file mode 100644
index bd7ed874a..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanIT.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.mistralai.autoconfigure.MistralAiAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.mistralai.MistralAiChatModel;
-import org.springframework.ai.mistralai.MistralAiChatOptions;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
-class PaymentStatusBeanIT {
-
- // Assuming we have the following data
- public static final Map DATA = Map.of("T1001", new StatusDate("Paid", "2021-10-05"), "T1002",
- new StatusDate("Unpaid", "2021-10-06"), "T1003", new StatusDate("Paid", "2021-10-07"), "T1004",
- new StatusDate("Paid", "2021-10-05"), "T1005", new StatusDate("Pending", "2021-10-08"));
-
- private final Logger logger = LoggerFactory.getLogger(PaymentStatusBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.apiKey=" + System.getenv("MISTRAL_AI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(MistralAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
-
- this.contextRunner
- .withPropertyValues("spring.ai.mistralai.chat.options.model=" + MistralAiApi.ChatModel.LARGE.getValue())
- .run(context -> {
-
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(new UserMessage("What's the status of my transaction with id T1001?")),
- MistralAiChatOptions.builder()
- .function("retrievePaymentStatus")
- .function("retrievePaymentDate")
- .build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("T1001");
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("paid");
- });
- }
-
- record StatusDate(String status, String date) {
-
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get payment status of a transaction")
- public Function retrievePaymentStatus() {
- return transaction -> new Status(DATA.get(transaction.transactionId).status());
- }
-
- @Bean
- @Description("Get payment date of a transaction")
- public Function retrievePaymentDate() {
- return transaction -> new Date(DATA.get(transaction.transactionId).date());
- }
-
- public record Transaction(@JsonProperty(required = true, value = "transaction_id") String transactionId) {
-
- }
-
- public record Status(@JsonProperty(required = true, value = "status") String status) {
-
- }
-
- public record Date(@JsonProperty(required = true, value = "date") String date) {
-
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanOpenAiIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanOpenAiIT.java
deleted file mode 100644
index 9c4b1c71a..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanOpenAiIT.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Same test as {@link PaymentStatusBeanIT.java} but using {@link OpenAiChatModel} for
- * Mistral AI Function Calling implementation.
- *
- * @author Christian Tzolov
- */
-@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
-class PaymentStatusBeanOpenAiIT {
-
- // Assuming we have the following data
- public static final Map DATA = Map.of("T1001", new StatusDate("Paid", "2021-10-05"), "T1002",
- new StatusDate("Unpaid", "2021-10-06"), "T1003", new StatusDate("Paid", "2021-10-07"), "T1004",
- new StatusDate("Paid", "2021-10-05"), "T1005", new StatusDate("Pending", "2021-10-08"));
-
- private final Logger logger = LoggerFactory.getLogger(PaymentStatusBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("MISTRAL_AI_API_KEY"),
- "spring.ai.openai.chat.base-url=https://api.mistral.ai")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
-
- this.contextRunner
- .withPropertyValues("spring.ai.openai.chat.options.model=" + MistralAiApi.ChatModel.SMALL.getValue())
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(new UserMessage("What's the status of my transaction with id T1001?")),
- OpenAiChatOptions.builder()
- .function("retrievePaymentStatus")
- .function("retrievePaymentDate")
- .build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("T1001");
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("paid");
- });
- }
-
- record StatusDate(String status, String date) {
-
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get payment status of a transaction")
- public Function retrievePaymentStatus() {
- return transaction -> new Status(DATA.get(transaction.transactionId).status());
- }
-
- @Bean
- @Description("Get payment date of a transaction")
- public Function retrievePaymentDate() {
- return transaction -> new Date(DATA.get(transaction.transactionId).date());
- }
-
- public record Transaction(@JsonProperty(required = true, value = "transaction_id") String transactionId) {
-
- }
-
- public record Status(@JsonProperty(required = true, value = "status") String status) {
-
- }
-
- public record Date(@JsonProperty(required = true, value = "date") String date) {
-
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusPromptIT.java
deleted file mode 100644
index 01a278a3d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusPromptIT.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.mistralai.autoconfigure.MistralAiAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.mistralai.MistralAiChatModel;
-import org.springframework.ai.mistralai.MistralAiChatOptions;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
-public class PaymentStatusPromptIT {
-
- // Assuming we have the following payment data.
- public static final Map DATA = Map.of(new Transaction("T1001"),
- new StatusDate("Paid", "2021-10-05"), new Transaction("T1002"), new StatusDate("Unpaid", "2021-10-06"),
- new Transaction("T1003"), new StatusDate("Paid", "2021-10-07"), new Transaction("T1004"),
- new StatusDate("Paid", "2021-10-05"), new Transaction("T1005"), new StatusDate("Pending", "2021-10-08"));
-
- private final Logger logger = LoggerFactory.getLogger(WeatherServicePromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.apiKey=" + System.getenv("MISTRAL_AI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(MistralAiAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues("spring.ai.mistralai.chat.options.model=" + MistralAiApi.ChatModel.SMALL.getValue())
- .run(context -> {
-
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
-
- UserMessage userMessage = new UserMessage("What's the status of my transaction with id T1001?");
-
- var promptOptions = MistralAiChatOptions.builder()
- .functionCallbacks(List.of(FunctionToolCallback
- .builder("retrievePaymentStatus",
- (Transaction transaction) -> new Status(DATA.get(transaction).status()))
- .description("Get payment status of a transaction")
- .inputType(Transaction.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("T1001");
- assertThat(response.getResult().getOutput().getText()).containsIgnoringCase("paid");
- });
- }
-
- public record Transaction(@JsonProperty(required = true, value = "transaction_id") String id) {
-
- }
-
- public record Status(@JsonProperty(required = true, value = "status") String status) {
-
- }
-
- record StatusDate(String status, String date) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/WeatherServicePromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/WeatherServicePromptIT.java
deleted file mode 100644
index 758ab779f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/WeatherServicePromptIT.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.model.mistralai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.mistralai.autoconfigure.MistralAiAutoConfiguration;
-import org.springframework.ai.model.mistralai.autoconfigure.tool.WeatherServicePromptIT.MyWeatherService.Request;
-import org.springframework.ai.model.mistralai.autoconfigure.tool.WeatherServicePromptIT.MyWeatherService.Response;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.mistralai.MistralAiChatModel;
-import org.springframework.ai.mistralai.MistralAiChatOptions;
-import org.springframework.ai.mistralai.api.MistralAiApi;
-import org.springframework.ai.mistralai.api.MistralAiApi.ChatCompletionRequest.ToolChoice;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-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
- * @author Alexandros Pappas
- * @since 0.8.1
- */
-@EnabledIfEnvironmentVariable(named = "MISTRAL_AI_API_KEY", matches = ".*")
-public class WeatherServicePromptIT {
-
- private final Logger logger = LoggerFactory.getLogger(WeatherServicePromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mistralai.api-key=" + System.getenv("MISTRAL_AI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(MistralAiAutoConfiguration.class));
-
- @Test
- void promptFunctionCall() {
- this.contextRunner
- .withPropertyValues("spring.ai.mistralai.chat.options.model=" + MistralAiApi.ChatModel.LARGE.getValue())
- .run(context -> {
-
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
-
- UserMessage userMessage = new UserMessage("What's the weather like in Paris? Use Celsius.");
- // UserMessage userMessage = new UserMessage("What's the weather like in
- // San Francisco, Tokyo, and
- // Paris?");
-
- var promptOptions = MistralAiChatOptions.builder()
- .toolChoice(ToolChoice.AUTO)
- .functionCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MyWeatherService())
- .description("Get the current weather in requested location")
- .inputType(MyWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsAnyOf("15", "15.0");
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner
- .withPropertyValues("spring.ai.mistralai.chat.options.model=" + MistralAiApi.ChatModel.LARGE.getValue())
- .run(context -> {
-
- MistralAiChatModel chatModel = context.getBean(MistralAiChatModel.class);
-
- UserMessage userMessage = new UserMessage("What's the weather like in Paris? Use Celsius.");
-
- ToolCallingChatOptions functionOptions = ToolCallingChatOptions.builder()
- .toolCallbacks(List.of(FunctionToolCallback.builder("CurrentWeatherService", new MyWeatherService())
- .description("Get the current weather in requested location")
- .inputType(MyWeatherService.Request.class)
- .build()))
-
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), functionOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).containsAnyOf("15", "15.0");
- });
- }
-
- public static class MyWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
- if (request.location().contains("Paris")) {
- return new Response(15, request.unit());
- }
- else if (request.location().contains("Tokyo")) {
- return new Response(10, request.unit());
- }
- else if (request.location().contains("San Francisco")) {
- return new Response(30, request.unit());
- }
- throw new IllegalArgumentException("Invalid request: " + request);
- }
-
- // @formatter:off
- public enum Unit { C, F }
-
- @JsonInclude(Include.NON_NULL)
- public record Request(
- @JsonProperty(required = true, value = "location") String location,
- @JsonProperty(required = true, value = "unit") Unit unit) { }
- // @formatter:on
-
- public record Response(double temperature, Unit unit) {
-
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotPropertiesTests.java
deleted file mode 100644
index c97df8b12..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/MoonshotPropertiesTests.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.moonshot.MoonshotChatModel;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-public class MoonshotPropertiesTests {
-
- @Test
- public void chatProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.moonshot.base-url=TEST_BASE_URL",
- "spring.ai.moonshot.api-key=abc123",
- "spring.ai.moonshot.chat.options.model=MODEL_XYZ",
- "spring.ai.moonshot.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MoonshotChatProperties.class);
- var connectionProperties = context.getBean(MoonshotCommonProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isNull();
- assertThat(chatProperties.getBaseUrl()).isNull();
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void chatOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.moonshot.base-url=TEST_BASE_URL",
- "spring.ai.moonshot.api-key=abc123",
- "spring.ai.moonshot.chat.base-url=TEST_BASE_URL2",
- "spring.ai.moonshot.chat.api-key=456",
- "spring.ai.moonshot.chat.options.model=MODEL_XYZ",
- "spring.ai.moonshot.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MoonshotChatProperties.class);
- var connectionProperties = context.getBean(MoonshotCommonProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isEqualTo("456");
- assertThat(chatProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.moonshot.api-key=API_KEY",
- "spring.ai.moonshot.base-url=TEST_BASE_URL",
-
- "spring.ai.moonshot.chat.options.model=MODEL_XYZ",
- "spring.ai.moonshot.chat.options.frequencyPenalty=-1.5",
- "spring.ai.moonshot.chat.options.logitBias.myTokenId=-5",
- "spring.ai.moonshot.chat.options.maxTokens=123",
- "spring.ai.moonshot.chat.options.n=10",
- "spring.ai.moonshot.chat.options.presencePenalty=0",
- "spring.ai.moonshot.chat.options.responseFormat.type=json",
- "spring.ai.moonshot.chat.options.seed=66",
- "spring.ai.moonshot.chat.options.stop=boza,koza",
- "spring.ai.moonshot.chat.options.temperature=0.55",
- "spring.ai.moonshot.chat.options.topP=0.56",
- "spring.ai.moonshot.chat.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(MoonshotChatProperties.class);
- var connectionProperties = context.getBean(MoonshotCommonProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5);
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getN()).isEqualTo(10);
- assertThat(chatProperties.getOptions().getPresencePenalty()).isEqualTo(0);
- assertThat(chatProperties.getOptions().getStop()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
-
- assertThat(chatProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- void chatActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.api-key=API_KEY", "spring.ai.moonshot.base-url=TEST_BASE_URL",
- "spring.ai.moonshot.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MoonshotChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MoonshotChatModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.api-key=API_KEY", "spring.ai.moonshot.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MoonshotChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MoonshotChatModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.api-key=API_KEY", "spring.ai.moonshot.base-url=TEST_BASE_URL",
- "spring.ai.moonshot.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(MoonshotChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(MoonshotChatModel.class)).isNotEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackInPromptIT.java
deleted file mode 100644
index 0dc2271f3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackInPromptIT.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure.tool;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.moonshot.autoconfigure.MoonshotAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.moonshot.MoonshotChatModel;
-import org.springframework.ai.moonshot.MoonshotChatOptions;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- * @author Alexandros Pappas
- */
-@EnabledIfEnvironmentVariable(named = "MOONSHOT_API_KEY", matches = ".*")
-public class FunctionCallbackInPromptIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.apiKey=" + System.getenv("MOONSHOT_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- var promptOptions = MoonshotChatOptions.builder()
- .functionCallbacks(List.of(FunctionCallback.builder()
- .function("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamingFunctionCallTest() {
-
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- var promptOptions = MoonshotChatOptions.builder()
- .functionCallbacks(List.of(FunctionCallback.builder()
- .function("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- Flux response = chatModel.stream(new Prompt(List.of(userMessage), promptOptions));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
deleted file mode 100644
index 72b3681c8..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure.tool;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.moonshot.autoconfigure.MoonshotAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.moonshot.MoonshotChatModel;
-import org.springframework.ai.moonshot.MoonshotChatOptions;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- * @author Alexandros Pappas
- */
-@EnabledIfEnvironmentVariable(named = "MOONSHOT_API_KEY", matches = ".*")
-class FunctionCallbackWithPlainFunctionBeanIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackWithPlainFunctionBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.apiKey=" + System.getenv("MOONSHOT_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- MoonshotChatOptions.builder().function("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.call(new Prompt(List.of(userMessage),
- MoonshotChatOptions.builder().function("weatherFunctionTwo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- ToolCallingChatOptions toolOptions = ToolCallingChatOptions.builder().toolNames("weatherFunction").build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), toolOptions));
-
- logger.info("Response: {}", response);
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- Flux response = chatModel.stream(new Prompt(List.of(userMessage),
- MoonshotChatOptions.builder().function("weatherFunction").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.stream(new Prompt(List.of(userMessage),
- MoonshotChatOptions.builder().function("weatherFunctionTwo").build()));
-
- content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get the weather in location")
- public Function weatherFunction() {
- return new MockWeatherService();
- }
-
- // Relies on the Request's JsonClassDescription annotation to provide the
- // function description.
- @Bean
- public Function weatherFunctionTwo() {
- MockWeatherService weatherService = new MockWeatherService();
- return (weatherService::apply);
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MockWeatherService.java
deleted file mode 100644
index 1fbc6858b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MockWeatherService.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure.tool;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Geng Rong
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 0;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Weather API request")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MoonshotFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MoonshotFunctionCallbackIT.java
deleted file mode 100644
index 86a85a9e1..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/moonshot/autoconfigure/tool/MoonshotFunctionCallbackIT.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.model.moonshot.autoconfigure.tool;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.moonshot.autoconfigure.MoonshotAutoConfiguration;
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.moonshot.MoonshotChatModel;
-import org.springframework.ai.moonshot.MoonshotChatOptions;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- * @author Alexandros Pappas
- */
-@EnabledIfEnvironmentVariable(named = "MOONSHOT_API_KEY", matches = ".*")
-public class MoonshotFunctionCallbackIT {
-
- private final Logger logger = LoggerFactory.getLogger(MoonshotFunctionCallbackIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.moonshot.apiKey=" + System.getenv("MOONSHOT_API_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, MoonshotAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), MoonshotChatOptions.builder().function("WeatherInfo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- MoonshotChatModel chatModel = context.getBean(MoonshotChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius");
-
- Flux response = chatModel.stream(
- new Prompt(List.of(userMessage), MoonshotChatOptions.builder().function("WeatherInfo").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .filter(Objects::nonNull)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public FunctionCallback weatherFunctionInfo() {
-
- return FunctionCallback.builder()
- .function("WeatherInfo", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAIAutoConfigurationTest.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAIAutoConfigurationTest.java
deleted file mode 100644
index b8935152e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAIAutoConfigurationTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-
-import com.oracle.bmc.http.client.pki.Pem;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import org.springframework.ai.oci.cohere.OCICohereChatModel;
-import org.springframework.ai.oci.cohere.OCICohereChatOptions;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class OCIGenAIAutoConfigurationTest {
-
- @Test
- void setProperties(@TempDir Path tempDir) throws Exception {
- Path tmp = tempDir.resolve("my-key.pem");
- createPrivateKey(tmp);
- ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.oci.genai.authenticationType=simple",
- "spring.ai.oci.genai.userId=my-user",
- "spring.ai.oci.genai.tenantId=my-tenant",
- "spring.ai.oci.genai.fingerprint=xyz",
- "spring.ai.oci.genai.privateKey=" + tmp.toAbsolutePath(),
- "spring.ai.oci.genai.region=us-ashburn-1",
- "spring.ai.oci.genai.cohere.chat.options.compartment=my-compartment",
- "spring.ai.oci.genai.cohere.chat.options.servingMode=dedicated",
- "spring.ai.oci.genai.cohere.chat.options.model=my-model",
- "spring.ai.oci.genai.cohere.chat.options.maxTokens=1000",
- "spring.ai.oci.genai.cohere.chat.options.temperature=0.5",
- "spring.ai.oci.genai.cohere.chat.options.topP=0.8",
- "spring.ai.oci.genai.cohere.chat.options.maxTokens=1000",
- "spring.ai.oci.genai.cohere.chat.options.frequencyPenalty=0.1",
- "spring.ai.oci.genai.cohere.chat.options.presencePenalty=0.2"
- // @formatter:on
- ).withConfiguration(AutoConfigurations.of(OCIGenAiAutoConfiguration.class));
-
- contextRunner.run(context -> {
- OCICohereChatModel chatModel = context.getBean(OCICohereChatModel.class);
- assertThat(chatModel).isNotNull();
- OCICohereChatOptions options = (OCICohereChatOptions) chatModel.getDefaultOptions();
- assertThat(options.getCompartment()).isEqualTo("my-compartment");
- assertThat(options.getModel()).isEqualTo("my-model");
- assertThat(options.getServingMode()).isEqualTo("dedicated");
- assertThat(options.getMaxTokens()).isEqualTo(1000);
- assertThat(options.getTemperature()).isEqualTo(0.5);
- assertThat(options.getTopP()).isEqualTo(0.8);
- assertThat(options.getFrequencyPenalty()).isEqualTo(0.1);
- assertThat(options.getPresencePenalty()).isEqualTo(0.2);
-
- OCIConnectionProperties props = context.getBean(OCIConnectionProperties.class);
- assertThat(props.getAuthenticationType()).isEqualTo(OCIConnectionProperties.AuthenticationType.SIMPLE);
- assertThat(props.getUserId()).isEqualTo("my-user");
- assertThat(props.getTenantId()).isEqualTo("my-tenant");
- assertThat(props.getFingerprint()).isEqualTo("xyz");
- assertThat(props.getPrivateKey()).isEqualTo(tmp.toAbsolutePath().toString());
- assertThat(props.getRegion()).isEqualTo("us-ashburn-1");
-
- });
- }
-
- private void createPrivateKey(Path tmp) throws Exception {
- KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
- gen.initialize(2048);
- KeyPair keyPair = gen.generateKeyPair();
- byte[] encoded = Pem.encoder().encode(keyPair.getPrivate());
- Files.write(tmp, encoded);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAiAutoConfigurationIT.java
deleted file mode 100644
index 610ef9c97..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/oci/genai/autoconfigure/OCIGenAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.model.oci.genai.autoconfigure;
-
-import java.nio.file.Paths;
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-
-import org.springframework.ai.embedding.EmbeddingRequest;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.oci.OCIEmbeddingModel;
-import org.springframework.ai.oci.cohere.OCICohereChatModel;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = OCIGenAiAutoConfigurationIT.COMPARTMENT_ID_KEY, matches = ".+")
-public class OCIGenAiAutoConfigurationIT {
-
- public static final String COMPARTMENT_ID_KEY = "OCI_COMPARTMENT_ID";
-
- public static final String OCI_CHAT_MODEL_ID_KEY = "OCI_CHAT_MODEL_ID";
-
- private final String CONFIG_FILE = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
-
- private final String COMPARTMENT_ID = System.getenv(COMPARTMENT_ID_KEY);
-
- private final String CHAT_MODEL_ID = System.getenv(OCI_CHAT_MODEL_ID_KEY);
-
- private final ApplicationContextRunner embeddingContextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.oci.genai.authenticationType=file",
- "spring.ai.oci.genai.file=" + this.CONFIG_FILE,
- "spring.ai.oci.genai.embedding.compartment=" + this.COMPARTMENT_ID,
- "spring.ai.oci.genai.embedding.servingMode=on-demand",
- "spring.ai.oci.genai.embedding.model=cohere.embed-english-light-v2.0"
- // @formatter:on
- ).withConfiguration(AutoConfigurations.of(OCIGenAiAutoConfiguration.class));
-
- private final ApplicationContextRunner cohereChatContextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.oci.genai.authenticationType=file",
- "spring.ai.oci.genai.file=" + this.CONFIG_FILE,
- "spring.ai.oci.genai.cohere.chat.options.compartment=" + this.COMPARTMENT_ID,
- "spring.ai.oci.genai.cohere.chat.options.servingMode=on-demand",
- "spring.ai.oci.genai.cohere.chat.options.model=" + this.CHAT_MODEL_ID
- // @formatter:on
- ).withConfiguration(AutoConfigurations.of(OCIGenAiAutoConfiguration.class));
-
- @Test
- void embeddings() {
- this.embeddingContextRunner.run(context -> {
- OCIEmbeddingModel embeddingModel = context.getBean(OCIEmbeddingModel.class);
- assertThat(embeddingModel).isNotNull();
- EmbeddingResponse response = embeddingModel
- .call(new EmbeddingRequest(List.of("There are 50 states in the USA", "Canada has 10 provinces"), null));
- assertThat(response).isNotNull();
- assertThat(response.getResults()).hasSize(2);
- });
- }
-
- @Test
- @EnabledIfEnvironmentVariable(named = OCIGenAiAutoConfigurationIT.OCI_CHAT_MODEL_ID_KEY, matches = ".+")
- void cohereChat() {
- this.cohereChatContextRunner.run(context -> {
- OCICohereChatModel chatModel = context.getBean(OCICohereChatModel.class);
- assertThat(chatModel).isNotNull();
- String response = chatModel.call("How many states are in the United States of America?");
- assertThat(response).isNotBlank();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java
deleted file mode 100644
index b57f2ccab..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/BaseOllamaIT.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import java.time.Duration;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.ollama.OllamaContainer;
-
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.management.ModelManagementOptions;
-import org.springframework.ai.ollama.management.OllamaModelManager;
-import org.springframework.ai.ollama.management.PullModelStrategy;
-import org.springframework.util.Assert;
-
-@Testcontainers
-@EnabledIfEnvironmentVariable(named = "OLLAMA_AUTOCONF_TESTS_ENABLED", matches = "true")
-public abstract class BaseOllamaIT {
-
- static {
- System.out.println("OLLAMA_AUTOCONF_TESTS_ENABLED=" + System.getenv("OLLAMA_AUTOCONF_TESTS_ENABLED"));
- System.out.println("System property=" + System.getProperty("OLLAMA_AUTOCONF_TESTS_ENABLED"));
- }
- private static final String OLLAMA_LOCAL_URL = "http://localhost:11434";
-
- private static final Duration DEFAULT_TIMEOUT = Duration.ofMinutes(10);
-
- private static final int DEFAULT_MAX_RETRIES = 2;
-
- // Environment variable to control whether to create a new container or use existing
- // Ollama instance
- private static final boolean SKIP_CONTAINER_CREATION = Boolean
- .parseBoolean(System.getenv().getOrDefault("OLLAMA_WITH_REUSE", "false"));
-
- private static OllamaContainer ollamaContainer;
-
- private static final ThreadLocal ollamaApi = new ThreadLocal<>();
-
- /**
- * Initialize the Ollama API with the specified model. When OLLAMA_WITH_REUSE=true
- * (default), uses TestContainers withReuse feature. When OLLAMA_WITH_REUSE=false,
- * connects to local Ollama instance.
- * @param model the Ollama model to initialize (must not be null or empty)
- * @return configured OllamaApi instance
- * @throws IllegalArgumentException if model is null or empty
- */
- protected static OllamaApi initializeOllama(final String model) {
- Assert.hasText(model, "Model name must be provided");
-
- if (!SKIP_CONTAINER_CREATION) {
- ollamaContainer = new OllamaContainer(OllamaImage.DEFAULT_IMAGE).withReuse(true);
- ollamaContainer.start();
- }
-
- final OllamaApi api = buildOllamaApiWithModel(model);
- ollamaApi.set(api);
- return api;
- }
-
- /**
- * Get the initialized OllamaApi instance.
- * @return the OllamaApi instance
- * @throws IllegalStateException if called before initialization
- */
- protected static OllamaApi getOllamaApi() {
- OllamaApi api = ollamaApi.get();
- Assert.state(api != null, "OllamaApi not initialized. Call initializeOllama first.");
- return api;
- }
-
- @AfterAll
- public static void tearDown() {
- if (ollamaContainer != null) {
- ollamaContainer.stop();
- }
- }
-
- public static OllamaApi buildOllamaApiWithModel(final String model) {
- final String baseUrl = SKIP_CONTAINER_CREATION ? OLLAMA_LOCAL_URL : ollamaContainer.getEndpoint();
- final OllamaApi api = new OllamaApi(baseUrl);
- ensureModelIsPresent(api, model);
- return api;
- }
-
- public String getBaseUrl() {
- String baseUrl = SKIP_CONTAINER_CREATION ? OLLAMA_LOCAL_URL : ollamaContainer.getEndpoint();
- return baseUrl;
- }
-
- private static void ensureModelIsPresent(final OllamaApi ollamaApi, final String model) {
- final var modelManagementOptions = ModelManagementOptions.builder()
- .maxRetries(DEFAULT_MAX_RETRIES)
- .timeout(DEFAULT_TIMEOUT)
- .build();
- final var ollamaModelManager = new OllamaModelManager(ollamaApi, modelManagementOptions);
- ollamaModelManager.pullModel(model, PullModelStrategy.WHEN_MISSING);
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationIT.java
deleted file mode 100644
index 1623d3ed3..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationIT.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.api.OllamaModel;
-import org.springframework.ai.ollama.management.OllamaModelManager;
-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
- * @author Eddú Meléndez
- * @author Thomas Vitale
- * @since 0.8.0
- */
-public class OllamaChatAutoConfigurationIT extends BaseOllamaIT {
-
- private static final String MODEL_NAME = OllamaModel.LLAMA3_2.getName();
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.baseUrl=" + getBaseUrl(),
- "spring.ai.ollama.chat.options.model=" + MODEL_NAME,
- "spring.ai.ollama.chat.options.temperature=0.5",
- "spring.ai.ollama.chat.options.topK=10")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class));
-
- private final UserMessage userMessage = new UserMessage("What's the capital of Denmark?");
-
- @BeforeAll
- public static void beforeAll() throws IOException, InterruptedException {
- initializeOllama(MODEL_NAME);
- }
-
- @Test
- public void chatCompletion() {
- this.contextRunner.run(context -> {
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
- ChatResponse response = chatModel.call(new Prompt(this.userMessage));
- assertThat(response.getResult().getOutput().getText()).contains("Copenhagen");
- });
- }
-
- @Test
- public void chatCompletionStreaming() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- Flux response = chatModel.stream(new Prompt(this.userMessage));
-
- List responses = response.collectList().block();
- assertThat(responses.size()).isGreaterThan(1);
-
- String stitchedResponseContent = responses.stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
-
- assertThat(stitchedResponseContent).contains("Copenhagen");
- });
- }
-
- @Test
- public void chatCompletionWithPull() {
- this.contextRunner.withPropertyValues("spring.ai.ollama.init.pull-model-strategy=when_missing")
- .withPropertyValues("spring.ai.ollama.chat.options.model=tinyllama")
- .run(context -> {
- var model = "tinyllama";
- OllamaApi ollamaApi = context.getBean(OllamaApi.class);
- var modelManager = new OllamaModelManager(ollamaApi);
- assertThat(modelManager.isModelAvailable(model)).isTrue();
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
- ChatResponse response = chatModel.call(new Prompt(this.userMessage));
- assertThat(response.getResult().getOutput().getText()).contains("Copenhagen");
- modelManager.deleteModel(model);
- });
- }
-
- @Test
- void chatActivation() {
- this.contextRunner.withPropertyValues("spring.ai.ollama.chat.enabled=false").run(context -> {
- assertThat(context.getBeansOfType(OllamaChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaChatModel.class)).isEmpty();
- });
-
- this.contextRunner.run(context -> {
- assertThat(context.getBeansOfType(OllamaChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaChatModel.class)).isNotEmpty();
- });
-
- this.contextRunner.withPropertyValues("spring.ai.ollama.chat.enabled=true").run(context -> {
- assertThat(context.getBeansOfType(OllamaChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaChatModel.class)).isNotEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationTests.java
deleted file mode 100644
index 077073ef9..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfigurationTests.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-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 OllamaChatAutoConfigurationTests {
-
- @Test
- public void propertiesTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.base-url=TEST_BASE_URL",
- "spring.ai.ollama.chat.options.model=MODEL_XYZ",
- "spring.ai.ollama.chat.options.temperature=0.55",
- "spring.ai.ollama.chat.options.topP=0.56",
- "spring.ai.ollama.chat.options.topK=123")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, OllamaAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OllamaChatProperties.class);
- var connectionProperties = context.getBean(OllamaConnectionProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getModel()).isEqualTo("MODEL_XYZ");
-
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
-
- assertThat(chatProperties.getOptions().getTopK()).isEqualTo(123);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationIT.java
deleted file mode 100644
index 1a38c32ae..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationIT.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import java.io.IOException;
-import java.util.List;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.ollama.OllamaEmbeddingModel;
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.api.OllamaModel;
-import org.springframework.ai.ollama.management.OllamaModelManager;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Christian Tzolov
- * @author Thomas Vitale
- * @since 1.0.0
- */
-public class OllamaEmbeddingAutoConfigurationIT extends BaseOllamaIT {
-
- private static final String MODEL_NAME = OllamaModel.NOMIC_EMBED_TEXT.getName();
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.ollama.embedding.options.model=" + MODEL_NAME,
- "spring.ai.ollama.base-url=" + getBaseUrl())
- .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, OllamaAutoConfiguration.class));
-
- @BeforeAll
- public static void beforeAll() throws IOException, InterruptedException {
- initializeOllama(MODEL_NAME);
- }
-
- @Test
- public void singleTextEmbedding() {
- this.contextRunner.run(context -> {
- OllamaEmbeddingModel embeddingModel = context.getBean(OllamaEmbeddingModel.class);
- assertThat(embeddingModel).isNotNull();
- EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
- assertThat(embeddingResponse.getResults()).hasSize(1);
- assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
- assertThat(embeddingModel.dimensions()).isEqualTo(768);
- });
- }
-
- @Test
- public void embeddingWithPull() {
- this.contextRunner.withPropertyValues("spring.ai.ollama.init.pull-model-strategy=when_missing")
- .withPropertyValues("spring.ai.ollama.embedding.options.model=all-minilm")
- .run(context -> {
- var model = "all-minilm";
- OllamaApi ollamaApi = context.getBean(OllamaApi.class);
- var modelManager = new OllamaModelManager(ollamaApi);
- assertThat(modelManager.isModelAvailable(model)).isTrue();
-
- OllamaEmbeddingModel embeddingModel = context.getBean(OllamaEmbeddingModel.class);
- EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(List.of("Hello World"));
- assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
- modelManager.deleteModel(model);
- });
- }
-
- @Test
- void embeddingActivation() {
- this.contextRunner.withPropertyValues("spring.ai.ollama.embedding.enabled=false").run(context -> {
- assertThat(context.getBeansOfType(OllamaEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaEmbeddingModel.class)).isEmpty();
- });
-
- this.contextRunner.run(context -> {
- assertThat(context.getBeansOfType(OllamaEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaEmbeddingModel.class)).isNotEmpty();
- });
-
- this.contextRunner.withPropertyValues("spring.ai.ollama.embedding.enabled=true").run(context -> {
- assertThat(context.getBeansOfType(OllamaEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OllamaEmbeddingModel.class)).isNotEmpty();
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationTests.java
deleted file mode 100644
index b29d88f8b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaEmbeddingAutoConfigurationTests.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-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 OllamaEmbeddingAutoConfigurationTests {
-
- @Test
- public void propertiesTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.base-url=TEST_BASE_URL",
- "spring.ai.ollama.embedding.options.model=MODEL_XYZ",
- "spring.ai.ollama.embedding.options.temperature=0.13",
- "spring.ai.ollama.embedding.options.topK=13"
- // @formatter:on
- )
- .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, OllamaAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(OllamaEmbeddingProperties.class);
- var connectionProperties = context.getBean(OllamaConnectionProperties.class);
-
- assertThat(embeddingProperties.getModel()).isEqualTo("MODEL_XYZ");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(embeddingProperties.getOptions().toMap()).containsKeys("temperature");
- assertThat(embeddingProperties.getOptions().toMap().get("temperature")).isEqualTo(0.13);
- assertThat(embeddingProperties.getOptions().getTopK()).isEqualTo(13);
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaImage.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaImage.java
deleted file mode 100644
index 5bb7547d0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/OllamaImage.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure;
-
-public final class OllamaImage {
-
- public static final String DEFAULT_IMAGE = "ollama/ollama:0.5.7";
-
- private OllamaImage() {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackInPromptIT.java
deleted file mode 100644
index 875a90742..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackInPromptIT.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure.tool;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.ollama.autoconfigure.BaseOllamaIT;
-import org.springframework.ai.model.ollama.autoconfigure.OllamaAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaOptions;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class FunctionCallbackInPromptIT extends BaseOllamaIT {
-
- private static final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class);
-
- private static final String MODEL_NAME = "qwen2.5:3b";
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.baseUrl=" + getBaseUrl(),
- "spring.ai.ollama.chat.options.model=" + MODEL_NAME,
- "spring.ai.ollama.chat.options.temperature=0.5",
- "spring.ai.ollama.chat.options.topK=10")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class));
-
- @BeforeAll
- public static void beforeAll() {
- initializeOllama(MODEL_NAME);
- }
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- var promptOptions = OllamaOptions.builder()
- .functionCallbacks(List.of(FunctionToolCallback
- .builder("CurrentWeatherService", new MockWeatherService())
- .description(
- "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamingFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- var promptOptions = OllamaOptions.builder()
- .functionCallbacks(List.of(FunctionToolCallback
- .builder("CurrentWeatherService", new MockWeatherService())
- .description(
- "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- Flux response = chatModel.stream(new Prompt(List.of(userMessage), promptOptions));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/MockWeatherService.java
deleted file mode 100644
index 6995eeed4..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/MockWeatherService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.model.ollama.autoconfigure.tool;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Christian Tzolov
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 10;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Weather API request")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
- @JsonProperty(required = true, value = "lat") @JsonPropertyDescription("The city latitude") double lat,
- @JsonProperty(required = true, value = "lon") @JsonPropertyDescription("The city longitude") double lon,
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionCallbackIT.java
deleted file mode 100644
index 48d32380d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionCallbackIT.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.ollama.autoconfigure.tool;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.ollama.autoconfigure.BaseOllamaIT;
-import org.springframework.ai.model.ollama.autoconfigure.OllamaAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaOptions;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-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 static org.assertj.core.api.Assertions.assertThat;
-
-public class OllamaFunctionCallbackIT extends BaseOllamaIT {
-
- private static final Logger logger = LoggerFactory.getLogger(OllamaFunctionCallbackIT.class);
-
- private static final String MODEL_NAME = "qwen2.5:3b";
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.baseUrl=" + getBaseUrl(),
- "spring.ai.ollama.chat.options.model=" + MODEL_NAME,
- "spring.ai.ollama.chat.options.temperature=0.5",
- "spring.ai.ollama.chat.options.topK=10")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @BeforeAll
- public static void beforeAll() {
- initializeOllama(MODEL_NAME);
- }
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), OllamaOptions.builder().function("WeatherInfo").build()));
-
- logger.info("Response: " + response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- Flux response = chatModel
- .stream(new Prompt(List.of(userMessage), OllamaOptions.builder().function("WeatherInfo").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: " + content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- ToolCallingChatOptions functionOptions = ToolCallingChatOptions.builder().toolNames("WeatherInfo").build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), functionOptions));
-
- logger.info("Response: " + response.getResult().getOutput().getText());
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ToolCallback weatherFunctionInfo() {
-
- return FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
- .description(
- "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java
deleted file mode 100644
index 947192863..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.ollama.autoconfigure.tool;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.ollama.autoconfigure.BaseOllamaIT;
-import org.springframework.ai.model.ollama.autoconfigure.OllamaAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaOptions;
-import org.springframework.ai.tool.ToolCallbacks;
-import org.springframework.ai.tool.annotation.Tool;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Integration tests for function-based tool calling in Ollama.
- *
- * @author Thomas Vitale
- */
-public class OllamaFunctionToolBeanIT extends BaseOllamaIT {
-
- private static final Logger logger = LoggerFactory.getLogger(OllamaFunctionToolBeanIT.class);
-
- private static final String MODEL_NAME = "qwen2.5:3b";
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.ollama.baseUrl=" + getBaseUrl(),
- "spring.ai.ollama.chat.options.model=" + MODEL_NAME,
- "spring.ai.ollama.chat.options.temperature=0.5",
- "spring.ai.ollama.chat.options.topK=10")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @BeforeAll
- public static void beforeAll() {
- initializeOllama(MODEL_NAME);
- }
-
- @Test
- void toolCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- MyTools myTools = context.getBean(MyTools.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OllamaOptions.builder().toolCallbacks(ToolCallbacks.from(myTools)).build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
-
- }
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), OllamaOptions.builder().toolNames("weatherInfo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- Flux response = chatModel
- .stream(new Prompt(List.of(userMessage), OllamaOptions.builder().function("weatherInfo").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner.run(context -> {
-
- OllamaChatModel chatModel = context.getBean(OllamaChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What are the weather conditions in San Francisco, Tokyo, and Paris? Find the temperature in Celsius for each of the three locations.");
-
- ToolCallingChatOptions functionOptions = ToolCallingChatOptions.builder().toolNames("weatherInfo").build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), functionOptions));
-
- logger.info("Response: {}", response.getResult().getOutput().getText());
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- static class MyTools {
-
- @Tool(description = "Find the weather conditions, and temperatures for a location, like a city or state.")
- public String weatherByLocation(String locationName) {
- int temperature = 0;
- if (locationName.equals("San Francisco")) {
- temperature = 30;
- }
- else if (locationName.equals("Tokyo")) {
- temperature = 10;
- }
- else if (locationName.equals("Paris")) {
- temperature = 15;
- }
- return "The temperature in " + locationName + " is " + temperature + " degrees Celsius.";
- }
-
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Find the weather conditions, forecasts, and temperatures for a location, like a city or state.")
- public Function weatherInfo() {
- return new MockWeatherService();
- }
-
- @Bean
- public MyTools myTools() {
- return new MyTools();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java
deleted file mode 100644
index e384541c0..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.metadata.Usage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-import org.springframework.ai.openai.OpenAiAudioSpeechModel;
-import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiEmbeddingModel;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.api.OpenAiApi;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class OpenAiAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(OpenAiAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class));
-
- @Test
- void chatCall() {
- this.contextRunner.run(context -> {
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
- String response = chatModel.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void chatCallAudioResponse() {
- this.contextRunner
- .withPropertyValues(
- "spring.ai.openai.chat.options.model=" + OpenAiApi.ChatModel.GPT_4_O_AUDIO_PREVIEW.getValue(),
- "spring.ai.openai.chat.options.output-modalities=text,audio",
- "spring.ai.openai.chat.options.output-audio.voice=ALLOY",
- "spring.ai.openai.chat.options.output-audio.format=WAV")
- .run(context -> {
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- ChatResponse response = chatModel
- .call(new Prompt(new UserMessage("Tell me joke about Spring Framework")));
- assertThat(response).isNotNull();
- logger.info("Response: " + response);
- // AudioPlayer.play(response.getResult().getOutput().getMedia().get(0).getDataAsByteArray());
- });
- }
-
- @Test
- void transcribe() {
- this.contextRunner.run(context -> {
- OpenAiAudioTranscriptionModel transcriptionModel = context.getBean(OpenAiAudioTranscriptionModel.class);
- Resource audioFile = new ClassPathResource("/speech/jfk.flac");
- String response = transcriptionModel.call(audioFile);
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void speech() {
- this.contextRunner.run(context -> {
- OpenAiAudioSpeechModel speechModel = context.getBean(OpenAiAudioSpeechModel.class);
- byte[] response = speechModel.call("H");
- assertThat(response).isNotNull();
- assertThat(verifyMp3FrameHeader(response))
- .withFailMessage("Expected MP3 frame header to be present in the response, but it was not found.")
- .isTrue();
- assertThat(response.length).isNotEqualTo(0);
-
- logger.debug("Response: " + Arrays.toString(response));
- });
- }
-
- public boolean verifyMp3FrameHeader(byte[] audioResponse) {
- // Check if the response is null or too short to contain a frame header
- if (audioResponse == null || audioResponse.length < 2) {
- return false;
- }
- // Check for the MP3 frame header
- // 0xFFE0 is the sync word for an MP3 frame (11 bits set to 1 followed by 3 bits
- // set to 0)
- return (audioResponse[0] & 0xFF) == 0xFF && (audioResponse[1] & 0xE0) == 0xE0;
- }
-
- @Test
- void generateStreaming() {
- this.contextRunner.run(context -> {
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
- String response = responseFlux.collectList()
- .block()
- .stream()
- .map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText())
- .collect(Collectors.joining());
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void streamingWithTokenUsage() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.stream-usage=true").run(context -> {
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- Flux responseFlux = chatModel.stream(new Prompt(new UserMessage("Hello")));
-
- Usage[] streamingTokenUsage = new Usage[1];
- String response = responseFlux.collectList().block().stream().map(chatResponse -> {
- streamingTokenUsage[0] = chatResponse.getMetadata().getUsage();
- return (chatResponse.getResult() != null) ? chatResponse.getResult().getOutput().getText() : "";
- }).collect(Collectors.joining());
-
- assertThat(streamingTokenUsage[0].getPromptTokens()).isGreaterThan(0);
- assertThat(streamingTokenUsage[0].getCompletionTokens()).isGreaterThan(0);
- assertThat(streamingTokenUsage[0].getTotalTokens()).isGreaterThan(0);
-
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- OpenAiEmbeddingModel embeddingModel = context.getBean(OpenAiEmbeddingModel.class);
-
- EmbeddingResponse embeddingResponse = embeddingModel
- .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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingModel.dimensions()).isEqualTo(1536);
- });
- }
-
- @Test
- void generateImage() {
- this.contextRunner.withPropertyValues("spring.ai.openai.image.options.size=1024x1024").run(context -> {
- OpenAiImageModel imageModel = context.getBean(OpenAiImageModel.class);
- ImageResponse imageResponse = imageModel.call(new ImagePrompt("forest"));
- assertThat(imageResponse.getResults()).hasSize(1);
- assertThat(imageResponse.getResult().getOutput().getUrl()).isNotEmpty();
- logger.info("Generated image: " + imageResponse.getResult().getOutput().getUrl());
- });
- }
-
- @Test
- void generateImageWithModel() {
- // The 256x256 size is supported by dall-e-2, but not by dall-e-3.
- this.contextRunner
- .withPropertyValues("spring.ai.openai.image.options.model=dall-e-2",
- "spring.ai.openai.image.options.size=256x256")
- .run(context -> {
- OpenAiImageModel imageModel = context.getBean(OpenAiImageModel.class);
- ImageResponse imageResponse = imageModel.call(new ImagePrompt("forest"));
- assertThat(imageResponse.getResults()).hasSize(1);
- assertThat(imageResponse.getResult().getOutput().getUrl()).isNotEmpty();
- logger.info("Generated image: " + imageResponse.getResult().getOutput().getUrl());
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java
deleted file mode 100644
index bbff01e58..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiPropertiesTests.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.skyscreamer.jsonassert.JSONCompareMode;
-
-import org.springframework.ai.model.ModelOptionsUtils;
-import org.springframework.ai.openai.OpenAiAudioSpeechModel;
-import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiEmbeddingModel;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.api.OpenAiApi;
-import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.ToolChoiceBuilder;
-import org.springframework.ai.openai.api.OpenAiAudioApi;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link OpenAiConnectionProperties}, {@link OpenAiChatProperties} and
- * {@link OpenAiEmbeddingProperties}.
- *
- * @author Christian Tzolov
- * @author Thomas Vitale
- * @since 0.8.0
- */
-public class OpenAiPropertiesTests {
-
- @Test
- public void chatProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.chat.options.model=MODEL_XYZ",
- "spring.ai.openai.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isNull();
- assertThat(chatProperties.getBaseUrl()).isNull();
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void transcriptionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.audio.transcription.options.model=MODEL_XYZ",
- "spring.ai.openai.audio.transcription.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var transcriptionProperties = context.getBean(OpenAiAudioTranscriptionProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(transcriptionProperties.getApiKey()).isNull();
- assertThat(transcriptionProperties.getBaseUrl()).isNull();
-
- assertThat(transcriptionProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(transcriptionProperties.getOptions().getTemperature()).isEqualTo(0.55f);
- });
- }
-
- @Test
- public void chatOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.chat.base-url=TEST_BASE_URL2",
- "spring.ai.openai.chat.api-key=456",
- "spring.ai.openai.chat.options.model=MODEL_XYZ",
- "spring.ai.openai.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isEqualTo("456");
- assertThat(chatProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void transcriptionOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.audio.transcription.base-url=TEST_BASE_URL2",
- "spring.ai.openai.audio.transcription.api-key=456",
- "spring.ai.openai.audio.transcription.options.model=MODEL_XYZ",
- "spring.ai.openai.audio.transcription.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var transcriptionProperties = context.getBean(OpenAiAudioTranscriptionProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(transcriptionProperties.getApiKey()).isEqualTo("456");
- assertThat(transcriptionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(transcriptionProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(transcriptionProperties.getOptions().getTemperature()).isEqualTo(0.55f);
- });
- }
-
- @Test
- public void speechProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.audio.speech.options.model=TTS_1",
- "spring.ai.openai.audio.speech.options.voice=alloy",
- "spring.ai.openai.audio.speech.options.response-format=mp3",
- "spring.ai.openai.audio.speech.options.speed=0.75")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var speechProperties = context.getBean(OpenAiAudioSpeechProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(speechProperties.getApiKey()).isNull();
- assertThat(speechProperties.getBaseUrl()).isNull();
-
- assertThat(speechProperties.getOptions().getModel()).isEqualTo("TTS_1");
- assertThat(speechProperties.getOptions().getVoice())
- .isEqualTo(OpenAiAudioApi.SpeechRequest.Voice.ALLOY);
- assertThat(speechProperties.getOptions().getResponseFormat())
- .isEqualTo(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.MP3);
- assertThat(speechProperties.getOptions().getSpeed()).isEqualTo(0.75f);
- });
- }
-
- @Test
- public void speechPropertiesTest() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.audio.speech.options.model=TTS_1",
- "spring.ai.openai.audio.speech.options.voice=alloy",
- "spring.ai.openai.audio.speech.options.response-format=mp3",
- "spring.ai.openai.audio.speech.options.speed=0.75")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var speechProperties = context.getBean(OpenAiAudioSpeechProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(speechProperties.getOptions().getModel()).isEqualTo("TTS_1");
- assertThat(speechProperties.getOptions().getVoice())
- .isEqualTo(OpenAiAudioApi.SpeechRequest.Voice.ALLOY);
- assertThat(speechProperties.getOptions().getResponseFormat())
- .isEqualTo(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.MP3);
- assertThat(speechProperties.getOptions().getSpeed()).isEqualTo(0.75f);
- });
- }
-
- @Test
- public void speechOverrideConnectionPropertiesTest() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.audio.speech.base-url=TEST_BASE_URL2",
- "spring.ai.openai.audio.speech.api-key=456",
- "spring.ai.openai.audio.speech.options.model=TTS_2",
- "spring.ai.openai.audio.speech.options.voice=echo",
- "spring.ai.openai.audio.speech.options.response-format=opus",
- "spring.ai.openai.audio.speech.options.speed=0.5")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var speechProperties = context.getBean(OpenAiAudioSpeechProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(speechProperties.getApiKey()).isEqualTo("456");
- assertThat(speechProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(speechProperties.getOptions().getModel()).isEqualTo("TTS_2");
- assertThat(speechProperties.getOptions().getVoice()).isEqualTo(OpenAiAudioApi.SpeechRequest.Voice.ECHO);
- assertThat(speechProperties.getOptions().getResponseFormat())
- .isEqualTo(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.OPUS);
- assertThat(speechProperties.getOptions().getSpeed()).isEqualTo(0.5f);
- });
- }
-
- @Test
- public void embeddingProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isNull();
- assertThat(embeddingProperties.getBaseUrl()).isNull();
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void embeddingOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.embedding.base-url=TEST_BASE_URL2",
- "spring.ai.openai.embedding.api-key=456",
- "spring.ai.openai.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isEqualTo("456");
- assertThat(embeddingProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void imageProperties() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.image.options.model=MODEL_XYZ",
- "spring.ai.openai.image.options.n=3")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(OpenAiImageProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(imageProperties.getApiKey()).isNull();
- assertThat(imageProperties.getBaseUrl()).isNull();
-
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- });
- }
-
- @Test
- public void imageOverrideConnectionProperties() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.api-key=abc123",
- "spring.ai.openai.image.base-url=TEST_BASE_URL2",
- "spring.ai.openai.image.api-key=456",
- "spring.ai.openai.image.options.model=MODEL_XYZ",
- "spring.ai.openai.image.options.n=3")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(OpenAiImageProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(imageProperties.getApiKey()).isEqualTo("456");
- assertThat(imageProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.chat.options.model=MODEL_XYZ",
- "spring.ai.openai.chat.options.frequencyPenalty=-1.5",
- "spring.ai.openai.chat.options.logitBias.myTokenId=-5",
- "spring.ai.openai.chat.options.maxTokens=123",
- "spring.ai.openai.chat.options.n=10",
- "spring.ai.openai.chat.options.presencePenalty=0",
- "spring.ai.openai.chat.options.seed=66",
- "spring.ai.openai.chat.options.stop=boza,koza",
- "spring.ai.openai.chat.options.temperature=0.55",
- "spring.ai.openai.chat.options.topP=0.56",
-
- // "spring.ai.openai.chat.options.toolChoice.functionName=toolChoiceFunctionName",
- "spring.ai.openai.chat.options.toolChoice=" + ModelOptionsUtils.toJsonString(ToolChoiceBuilder.FUNCTION("toolChoiceFunctionName")),
-
- "spring.ai.openai.chat.options.tools[0].function.name=myFunction1",
- "spring.ai.openai.chat.options.tools[0].function.description=function description",
- "spring.ai.openai.chat.options.tools[0].function.jsonSchema=" + """
- {
- "type": "object",
- "properties": {
- "location": {
- "type": "string",
- "description": "The city and state e.g. San Francisco, CA"
- },
- "lat": {
- "type": "number",
- "description": "The city latitude"
- },
- "lon": {
- "type": "number",
- "description": "The city longitude"
- },
- "unit": {
- "type": "string",
- "enum": ["c", "f"]
- }
- },
- "required": ["location", "lat", "lon", "unit"]
- }
- """,
- "spring.ai.openai.chat.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("text-embedding-ada-002");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5);
- assertThat(chatProperties.getOptions().getLogitBias().get("myTokenId")).isEqualTo(-5);
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getN()).isEqualTo(10);
- assertThat(chatProperties.getOptions().getPresencePenalty()).isEqualTo(0);
- assertThat(chatProperties.getOptions().getSeed()).isEqualTo(66);
- assertThat(chatProperties.getOptions().getStop()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
-
- JSONAssert.assertEquals("{\"type\":\"function\",\"function\":{\"name\":\"toolChoiceFunctionName\"}}",
- "" + chatProperties.getOptions().getToolChoice(), JSONCompareMode.LENIENT);
-
- assertThat(chatProperties.getOptions().getUser()).isEqualTo("userXYZ");
-
- assertThat(chatProperties.getOptions().getTools()).hasSize(1);
- var tool = chatProperties.getOptions().getTools().get(0);
- assertThat(tool.getType()).isEqualTo(OpenAiApi.FunctionTool.Type.FUNCTION);
- var function = tool.getFunction();
- assertThat(function.getName()).isEqualTo("myFunction1");
- assertThat(function.getDescription()).isEqualTo("function description");
- assertThat(function.getParameters()).isNotEmpty();
- });
- }
-
- @Test
- public void transcriptionOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.audio.transcription.options.model=MODEL_XYZ",
- "spring.ai.openai.audio.transcription.options.language=en",
- "spring.ai.openai.audio.transcription.options.prompt=Er, yes, I think so",
- "spring.ai.openai.audio.transcription.options.responseFormat=JSON",
- "spring.ai.openai.audio.transcription.options.temperature=0.55"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var transcriptionProperties = context.getBean(OpenAiAudioTranscriptionProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("text-embedding-ada-002");
-
- assertThat(transcriptionProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(transcriptionProperties.getOptions().getLanguage()).isEqualTo("en");
- assertThat(transcriptionProperties.getOptions().getPrompt()).isEqualTo("Er, yes, I think so");
- assertThat(transcriptionProperties.getOptions().getResponseFormat())
- .isEqualTo(OpenAiAudioApi.TranscriptResponseFormat.JSON);
- assertThat(transcriptionProperties.getOptions().getTemperature()).isEqualTo(0.55f);
- });
- }
-
- @Test
- public void embeddingOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.embedding.options.model=MODEL_XYZ",
- "spring.ai.openai.embedding.options.encodingFormat=MyEncodingFormat",
- "spring.ai.openai.embedding.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(embeddingProperties.getOptions().getEncodingFormat()).isEqualTo("MyEncodingFormat");
- assertThat(embeddingProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- public void imageOptionsTest() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.image.options.n=3",
- "spring.ai.openai.image.options.model=MODEL_XYZ",
- "spring.ai.openai.image.options.quality=hd",
- "spring.ai.openai.image.options.response_format=url",
- "spring.ai.openai.image.options.size=1024x1024",
- "spring.ai.openai.image.options.width=1024",
- "spring.ai.openai.image.options.height=1024",
- "spring.ai.openai.image.options.style=vivid",
- "spring.ai.openai.image.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(OpenAiImageProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getQuality()).isEqualTo("hd");
- assertThat(imageProperties.getOptions().getResponseFormat()).isEqualTo("url");
- assertThat(imageProperties.getOptions().getSize()).isEqualTo("1024x1024");
- assertThat(imageProperties.getOptions().getWidth()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getHeight()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getStyle()).isEqualTo("vivid");
- assertThat(imageProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- void embeddingActivation() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isNotEmpty();
- });
- }
-
- @Test
- void chatActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void imageActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.image.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.image.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void audioSpeechActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.speech.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.speech.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void audioTranscriptionActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.transcription.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.transcription.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isNotEmpty();
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java
deleted file mode 100644
index a863c88b7..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/OpenAiResponseFormatPropertiesTests.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * 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.model.openai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.openai.OpenAiAudioSpeechModel;
-import org.springframework.ai.openai.OpenAiAudioTranscriptionModel;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiEmbeddingModel;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.api.OpenAiAudioApi;
-import org.springframework.ai.openai.api.ResponseFormat;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link OpenAiChatProperties} #options#responseFormat support.
- *
- * @author Christian Tzolov
- */
-public class OpenAiResponseFormatPropertiesTests {
-
- @Test
- public void responseFormatJsonSchema() {
-
- String responseFormatJsonSchema = """
- {
- "$schema" : "https://json-schema.org/draft/2020-12/schema",
- "type" : "object",
- "properties" : {
- "someString" : {
- "type" : "string"
- }
- },
- "additionalProperties" : false
- }
- """;
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
-
- "spring.ai.openai.chat.options.response-format.type=JSON_SCHEMA",
- "spring.ai.openai.chat.options.response-format.name=MyName",
- "spring.ai.openai.chat.options.response-format.schema=" + responseFormatJsonSchema,
- "spring.ai.openai.chat.options.response-format.strict=true"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(chatProperties.getOptions().getResponseFormat())
- .isEqualTo(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, responseFormatJsonSchema));
- });
- }
-
- @Test
- public void responseFormatJsonObject() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.chat.options.response-format.type=JSON_OBJECT")
-
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
-
- assertThat(chatProperties.getOptions().getResponseFormat())
- .isEqualTo(ResponseFormat.builder().type(ResponseFormat.Type.JSON_OBJECT).build());
- });
- }
-
- @Test
- public void emptyResponseFormat() {
-
- new ApplicationContextRunner().withPropertyValues("spring.ai.openai.api-key=API_KEY")
-
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(OpenAiChatProperties.class);
-
- assertThat(chatProperties.getOptions().getResponseFormat()).isNull();
- });
- }
-
- @Test
- public void transcriptionOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.audio.transcription.options.model=MODEL_XYZ",
- "spring.ai.openai.audio.transcription.options.language=en",
- "spring.ai.openai.audio.transcription.options.prompt=Er, yes, I think so",
- "spring.ai.openai.audio.transcription.options.responseFormat=JSON",
- "spring.ai.openai.audio.transcription.options.temperature=0.55"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var transcriptionProperties = context.getBean(OpenAiAudioTranscriptionProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("text-embedding-ada-002");
-
- assertThat(transcriptionProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(transcriptionProperties.getOptions().getLanguage()).isEqualTo("en");
- assertThat(transcriptionProperties.getOptions().getPrompt()).isEqualTo("Er, yes, I think so");
- assertThat(transcriptionProperties.getOptions().getResponseFormat())
- .isEqualTo(OpenAiAudioApi.TranscriptResponseFormat.JSON);
- assertThat(transcriptionProperties.getOptions().getTemperature()).isEqualTo(0.55f);
- });
- }
-
- @Test
- public void embeddingOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.embedding.options.model=MODEL_XYZ",
- "spring.ai.openai.embedding.options.encodingFormat=MyEncodingFormat",
- "spring.ai.openai.embedding.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
- var embeddingProperties = context.getBean(OpenAiEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(embeddingProperties.getOptions().getEncodingFormat()).isEqualTo("MyEncodingFormat");
- assertThat(embeddingProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- public void imageOptionsTest() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.openai.api-key=API_KEY",
- "spring.ai.openai.base-url=TEST_BASE_URL",
-
- "spring.ai.openai.image.options.n=3",
- "spring.ai.openai.image.options.model=MODEL_XYZ",
- "spring.ai.openai.image.options.quality=hd",
- "spring.ai.openai.image.options.response_format=url",
- "spring.ai.openai.image.options.size=1024x1024",
- "spring.ai.openai.image.options.width=1024",
- "spring.ai.openai.image.options.height=1024",
- "spring.ai.openai.image.options.style=vivid",
- "spring.ai.openai.image.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(OpenAiImageProperties.class);
- var connectionProperties = context.getBean(OpenAiConnectionProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
-
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getQuality()).isEqualTo("hd");
- assertThat(imageProperties.getOptions().getResponseFormat()).isEqualTo("url");
- assertThat(imageProperties.getOptions().getSize()).isEqualTo("1024x1024");
- assertThat(imageProperties.getOptions().getWidth()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getHeight()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getStyle()).isEqualTo("vivid");
- assertThat(imageProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- void embeddingActivation() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiEmbeddingModel.class)).isNotEmpty();
- });
- }
-
- @Test
- void chatActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiChatModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void imageActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.image.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.image.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiImageModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void audioSpeechActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.speech.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.speech.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioSpeechProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioSpeechModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- void audioTranscriptionActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.transcription.enabled=false")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.api-key=API_KEY", "spring.ai.openai.base-url=TEST_BASE_URL",
- "spring.ai.openai.audio.transcription.enabled=true")
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(OpenAiAudioTranscriptionModel.class)).isNotEmpty();
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java
deleted file mode 100644
index 8e238be52..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPrompt2IT.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.model.openai.autoconfigure.tool;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class FunctionCallbackInPrompt2IT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- ChatClient chatClient = ChatClient.builder(chatModel).build();
-
- // @formatter:off
- chatClient.prompt()
- .user("Tell me a joke?")
- .call().content();
-
- String content = ChatClient.builder(chatModel).build().prompt()
- .user("What's the weather like in San Francisco, Tokyo, and Paris?")
- .functions(FunctionToolCallback
- .builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build())
- .call().content();
- // @formatter:on
-
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
- @Test
- void lambdaFunctionCallTest() {
- Map state = new ConcurrentHashMap<>();
-
- record LightInfo(String roomName, boolean isOn) {
- }
-
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // @formatter:off
- String content = ChatClient.builder(chatModel).build().prompt()
- .user("Turn the light on in the kitchen and in the living room!")
- .functions(FunctionToolCallback
- .builder("turnLight", (LightInfo lightInfo) -> {
- logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName());
- state.put(lightInfo.roomName(), lightInfo.isOn());
- })
- .description("Turn light on or off in a room")
- .inputType(LightInfo.class)
- .build())
- .call().content();
- // @formatter:on
- logger.info("Response: {}", content);
- assertThat(state).containsEntry("kitchen", Boolean.TRUE);
- assertThat(state).containsEntry("living room", Boolean.TRUE);
- });
- }
-
- @Test
- void functionCallTest2() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // @formatter:off
- String content = ChatClient.builder(chatModel).build().prompt()
- .user("What's the weather like in Amsterdam?")
- .functions(FunctionToolCallback
- .builder("CurrentWeatherService", input -> "18 degrees Celsius")
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build())
- .call().content();
- // @formatter:on
- logger.info("Response: {}", content);
-
- assertThat(content).contains("18");
- });
- }
-
- @Test
- void streamingFunctionCallTest() {
-
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // @formatter:off
- String content = ChatClient.builder(chatModel).build().prompt()
- .user("What's the weather like in San Francisco, Tokyo, and Paris?")
- .functions(FunctionToolCallback
- .builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build())
- .stream().content()
- .collectList().block().stream().collect(Collectors.joining());
- // @formatter:on
-
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java
deleted file mode 100644
index 45596ff5e..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.model.openai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class FunctionCallbackInPromptIT {
-
- private final Logger logger = LoggerFactory.getLogger(FunctionCallbackInPromptIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"))
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class));
-
- @Test
- void functionCallTest() {
- this.contextRunner
- .withPropertyValues("spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName(),
- "spring.ai.openai.chat.options.temperature=0.1")
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris?");
-
- var promptOptions = OpenAiChatOptions.builder()
- .functionCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamingFunctionCallTest() {
-
- this.contextRunner
- .withPropertyValues("spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName(),
- "spring.ai.openai.chat.options.temperature=0.5")
- .run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris?");
-
- var promptOptions = OpenAiChatOptions.builder()
- .functionCallbacks(
- List.of(FunctionToolCallback.builder("CurrentWeatherService", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build()))
- .build();
-
- Flux response = chatModel.stream(new Prompt(List.of(userMessage), promptOptions));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
deleted file mode 100644
index b1ddf745d..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.openai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.model.ToolContext;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.model.tool.ToolCallingChatOptions;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
-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.context.annotation.Description;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-class FunctionCallbackWithPlainFunctionBeanIT {
-
- private static final Logger logger = LoggerFactory.getLogger(FunctionCallbackWithPlainFunctionBeanIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"),
- "spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- private static Map feedback = new ConcurrentHashMap<>();
-
- @BeforeEach
- void setUp() {
- feedback.clear();
- }
-
- @Test
- void functionCallingVoidInput() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage("Turn the light on in the living room");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder().function("turnLivingRoomLightOn").build()));
-
- logger.info("Response: {}", response);
- assertThat(feedback).hasSize(1);
- assertThat(feedback.get("turnLivingRoomLightOn")).isEqualTo(Boolean.valueOf(true));
- });
- }
-
- @Test
- void functionCallingSupplier() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage("Turn the light on in the living room");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder().function("turnLivingRoomLightOnSupplier").build()));
-
- logger.info("Response: {}", response);
- assertThat(feedback).hasSize(1);
- assertThat(feedback.get("turnLivingRoomLightOnSupplier")).isEqualTo(Boolean.valueOf(true));
- });
- }
-
- @Test
- void functionCallingVoidOutput() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage("Turn the light on in the kitchen and in the living room");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().function("turnLight").build()));
-
- logger.info("Response: {}", response);
- assertThat(feedback).hasSize(2);
- assertThat(feedback.get("kitchen")).isEqualTo(Boolean.valueOf(true));
- assertThat(feedback.get("living room")).isEqualTo(Boolean.valueOf(true));
- });
- }
-
- @Test
- void functionCallingConsumer() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage("Turn the light on in the kitchen and in the living room");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder().function("turnLightConsumer").build()));
-
- logger.info("Response: {}", response);
- assertThat(feedback).hasSize(2);
- assertThat(feedback.get("kitchen")).isEqualTo(Boolean.valueOf(true));
- assertThat(feedback.get("living room")).isEqualTo(Boolean.valueOf(true));
-
- });
- }
-
- @Test
- void trainScheduler() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "Please schedule a train from San Francisco to Los Angeles on 2023-12-25");
-
- ToolCallingChatOptions functionOptions = ToolCallingChatOptions.builder()
- .toolNames("trainReservation")
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), functionOptions));
-
- logger.info("Response: {}", response.getResult().getOutput().getText());
- });
- }
-
- @Test
- void functionCallWithDirectBiFunction() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- ChatClient chatClient = ChatClient.builder(chatModel).build();
-
- String content = chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?")
- .functions("weatherFunctionWithContext")
- .toolContext(Map.of("sessionId", "123"))
- .call()
- .content();
- logger.info(content);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? You can call the following functions 'weatherFunction'");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder()
- .function("weatherFunctionWithContext")
- .toolContext(Map.of("sessionId", "123"))
- .build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithBiFunctionClass() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- ChatClient chatClient = ChatClient.builder(chatModel).build();
-
- String content = chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?")
- .functions("weatherFunctionWithClassBiFunction")
- .toolContext(Map.of("sessionId", "123"))
- .call()
- .content();
- logger.info(content);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? You can call the following functions 'weatherFunction'");
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder()
- .function("weatherFunctionWithClassBiFunction")
- .toolContext(Map.of("sessionId", "123"))
- .build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallTest() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? You can call the following functions 'weatherFunction'");
-
- ChatResponse response = chatModel.call(
- new Prompt(List.of(userMessage), OpenAiChatOptions.builder().function("weatherFunction").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.call(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder().function("weatherFunctionTwo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void functionCallWithPortableFunctionCallingOptions() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
-
- ToolCallingChatOptions functionOptions = ToolCallingChatOptions.builder()
- .toolNames("weatherFunction")
- .build();
-
- ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), functionOptions));
-
- logger.info("Response: {}", response.getResult().getOutput().getText());
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // Test weatherFunction
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? You can call the following functions 'weatherFunction'");
-
- Flux response = chatModel.stream(
- new Prompt(List.of(userMessage), OpenAiChatOptions.builder().function("weatherFunction").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
-
- // Test weatherFunctionTwo
- response = chatModel.stream(new Prompt(List.of(userMessage),
- OpenAiChatOptions.builder().function("weatherFunctionTwo").build()));
-
- content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).isNotEmpty().withFailMessage("Content returned from OpenAI model is empty");
- assertThat(content).contains("30", "10", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- @Description("Get the weather in location")
- public MyBiFunction weatherFunctionWithClassBiFunction() {
- return new MyBiFunction();
- }
-
- @Bean
- @Description("Get the weather in location")
- public BiFunction weatherFunctionWithContext() {
- return (request, context) -> new MockWeatherService().apply(request);
- }
-
- @Bean
- @Description("Get the weather in location")
- public Function weatherFunction() {
- return new MockWeatherService();
- }
-
- // Relies on the Request's JsonClassDescription annotation to provide the
- // function description.
- @Bean
- public Function weatherFunctionTwo() {
- MockWeatherService weatherService = new MockWeatherService();
- return (weatherService::apply);
- }
-
- @Bean
- @Description("Turn light on or off in a room")
- public Function turnLight() {
- return (LightInfo lightInfo) -> {
- logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName());
- feedback.put(lightInfo.roomName(), lightInfo.isOn());
- return null;
- };
- }
-
- @Bean
- @Description("Turn light on or off in a room")
- public Consumer turnLightConsumer() {
- return (LightInfo lightInfo) -> {
- logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName());
- feedback.put(lightInfo.roomName(), lightInfo.isOn());
- };
- }
-
- @Bean
- @Description("Turns light on in the living room")
- public Function turnLivingRoomLightOn() {
- return (Void v) -> {
- logger.info("Turning light on in the living room");
- feedback.put("turnLivingRoomLightOn", Boolean.TRUE);
- return "Done";
- };
- }
-
- @Bean
- @Description("Turns light on in the living room")
- public Supplier turnLivingRoomLightOnSupplier() {
- return () -> {
- logger.info("Turning light on in the living room");
- feedback.put("turnLivingRoomLightOnSupplier", Boolean.TRUE);
- return "Done";
- };
- }
-
- @Bean
- @Description("Schedule a train reservation")
- public Function, TrainSearchResponse> trainReservation() {
- return (TrainSearchRequest request) -> {
- logger.info("Turning light to [" + request.data().from() + "] in " + request.data().to());
- return new TrainSearchResponse<>(
- new TrainSearchScheduleResponse(request.data().from(), request.data().to(), "", "123"));
- };
- }
-
- }
-
- public static class MyBiFunction
- implements BiFunction {
-
- @Override
- public MockWeatherService.Response apply(MockWeatherService.Request request, ToolContext context) {
- return new MockWeatherService().apply(request);
- }
-
- }
-
- record LightInfo(String roomName, boolean isOn) {
- }
-
- record TrainSearchSchedule(String from, String to, String date) {
- }
-
- record TrainSearchScheduleResponse(String from, String to, String date, String trainNumber) {
- }
-
- record TrainSearchRequest(T data) {
- }
-
- record TrainSearchResponse(T data) {
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/MockWeatherService.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/MockWeatherService.java
deleted file mode 100644
index f3f24a06f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/MockWeatherService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.model.openai.autoconfigure.tool;
-
-import java.util.function.Function;
-
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-
-/**
- * Mock 3rd party weather service.
- *
- * @author Christian Tzolov
- */
-public class MockWeatherService implements Function {
-
- @Override
- public Response apply(Request request) {
-
- double temperature = 10;
- if (request.location().contains("Paris")) {
- temperature = 15;
- }
- else if (request.location().contains("Tokyo")) {
- temperature = 10;
- }
- else if (request.location().contains("San Francisco")) {
- temperature = 30;
- }
-
- return new Response(temperature, 15, 20, 2, 53, 45, Unit.C);
- }
-
- /**
- * Temperature units.
- */
- public enum Unit {
-
- /**
- * Celsius.
- */
- C("metric"),
- /**
- * Fahrenheit.
- */
- F("imperial");
-
- /**
- * Human readable unit name.
- */
- public final String unitName;
-
- Unit(String text) {
- this.unitName = text;
- }
-
- }
-
- /**
- * Weather Function request.
- */
- @JsonInclude(Include.NON_NULL)
- @JsonClassDescription("Weather API request")
- public record Request(@JsonProperty(required = true,
- value = "location") @JsonPropertyDescription("The city and state e.g. San Francisco, CA") String location,
- @JsonProperty(required = true, value = "lat") @JsonPropertyDescription("The city latitude") double lat,
- @JsonProperty(required = true, value = "lon") @JsonPropertyDescription("The city longitude") double lon,
- @JsonProperty(required = true, value = "unit") @JsonPropertyDescription("Temperature unit") Unit unit) {
-
- }
-
- /**
- * Weather Function response.
- */
- public record Response(double temp, double feels_like, double temp_min, double temp_max, int pressure, int humidity,
- Unit unit) {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java
deleted file mode 100644
index 682a8dacb..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallback2IT.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.model.openai.autoconfigure.tool;
-
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-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 static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class OpenAiFunctionCallback2IT {
-
- private final Logger logger = LoggerFactory.getLogger(OpenAiFunctionCallback2IT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"),
- "spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.temperature=0.1").run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // @formatter:off
- ChatClient chatClient = ChatClient.builder(chatModel)
- .defaultFunctions("WeatherInfo")
- .defaultUser(u -> u.text("What's the weather like in {cities}?"))
- .build();
-
- String content = chatClient.prompt()
- .user(u -> u.param("cities", "San Francisco, Tokyo, Paris"))
- .call().content();
- // @formatter:on
-
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.temperature=0.1").run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- // @formatter:off
- String content = ChatClient.builder(chatModel).build().prompt()
- .functions("WeatherInfo")
- .user("What's the weather like in San Francisco, Tokyo, and Paris?")
- .stream().content()
- .collectList().block().stream().collect(Collectors.joining());
- // @formatter:on
-
- logger.info("Response: {}", content);
-
- assertThat(content).contains("30", "10", "15");
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ToolCallback weatherFunctionInfo() {
-
- return FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java
deleted file mode 100644
index 6ab701eb2..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/OpenAiFunctionCallbackIT.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.model.openai.autoconfigure.tool;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.model.openai.autoconfigure.OpenAiAutoConfiguration;
-import org.springframework.ai.chat.messages.AssistantMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-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 static org.assertj.core.api.Assertions.assertThat;
-
-@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
-public class OpenAiFunctionCallbackIT {
-
- private final Logger logger = LoggerFactory.getLogger(OpenAiFunctionCallbackIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.openai.apiKey=" + System.getenv("OPENAI_API_KEY"),
- "spring.ai.openai.chat.options.model=" + ChatModel.GPT_4_O_MINI.getName())
- .withConfiguration(AutoConfigurations.of(OpenAiAutoConfiguration.class))
- .withUserConfiguration(Config.class);
-
- @Test
- void functionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.temperature=0.1").run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
-
- ChatResponse response = chatModel
- .call(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().function("WeatherInfo").build()));
-
- logger.info("Response: {}", response);
-
- assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
-
- });
- }
-
- @Test
- void streamFunctionCallTest() {
- this.contextRunner.withPropertyValues("spring.ai.openai.chat.options.temperature=0.1").run(context -> {
-
- OpenAiChatModel chatModel = context.getBean(OpenAiChatModel.class);
-
- UserMessage userMessage = new UserMessage(
- "What's the weather like in San Francisco, Tokyo, and Paris? You can call the following functions 'WeatherInfo'");
-
- Flux response = chatModel
- .stream(new Prompt(List.of(userMessage), OpenAiChatOptions.builder().function("WeatherInfo").build()));
-
- String content = response.collectList()
- .block()
- .stream()
- .map(ChatResponse::getResults)
- .flatMap(List::stream)
- .map(Generation::getOutput)
- .map(AssistantMessage::getText)
- .collect(Collectors.joining());
- logger.info("Response: {}", content);
-
- assertThat(content).containsAnyOf("30.0", "30");
- assertThat(content).containsAnyOf("10.0", "10");
- assertThat(content).containsAnyOf("15.0", "15");
-
- });
- }
-
- @Configuration
- static class Config {
-
- @Bean
- public ToolCallback weatherFunctionInfo() {
-
- return FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
- .description("Get the weather in location")
- .inputType(MockWeatherService.Request.class)
- .build();
- }
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingPropertiesTests.java
deleted file mode 100644
index 9ac0dd861..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/postgresml/autoconfigure/PostgresMlEmbeddingPropertiesTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.model.postgresml.autoconfigure;
-
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.document.MetadataMode;
-import org.springframework.ai.postgresml.PostgresMlEmbeddingModel;
-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 PostgresMlEmbeddingProperties}.
- *
- * @author Utkarsh Srivastava
- * @author Christian Tzolov
- */
-@SpringBootTest(properties = { "spring.ai.postgresml.embedding.options.metadata-mode=all",
- "spring.ai.postgresml.embedding.options.kwargs.key1=value1",
- "spring.ai.postgresml.embedding.options.kwargs.key2=value2",
- "spring.ai.postgresml.embedding.options.transformer=abc123" })
-class PostgresMlEmbeddingPropertiesTests {
-
- @Autowired
- private PostgresMlEmbeddingProperties postgresMlProperties;
-
- @Test
- void postgresMlPropertiesAreCorrect() {
- assertThat(this.postgresMlProperties).isNotNull();
- assertThat(this.postgresMlProperties.getOptions().getTransformer()).isEqualTo("abc123");
- assertThat(this.postgresMlProperties.getOptions().getVectorType())
- .isEqualTo(PostgresMlEmbeddingModel.VectorType.PG_ARRAY);
- assertThat(this.postgresMlProperties.getOptions().getKwargs())
- .isEqualTo(Map.of("key1", "value1", "key2", "value2"));
- assertThat(this.postgresMlProperties.getOptions().getMetadataMode()).isEqualTo(MetadataMode.ALL);
- }
-
- @SpringBootConfiguration
- @EnableConfigurationProperties(PostgresMlEmbeddingProperties.class)
- static class TestConfiguration {
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfigurationIT.java
deleted file mode 100644
index bc2334e20..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanAutoConfigurationIT.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariables;
-import reactor.core.publisher.Flux;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.embedding.EmbeddingResponse;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-import org.springframework.ai.qianfan.QianFanChatModel;
-import org.springframework.ai.qianfan.QianFanEmbeddingModel;
-import org.springframework.ai.qianfan.QianFanImageModel;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Geng Rong
- */
-@EnabledIfEnvironmentVariables({ @EnabledIfEnvironmentVariable(named = "QIANFAN_API_KEY", matches = ".+"),
- @EnabledIfEnvironmentVariable(named = "QIANFAN_SECRET_KEY", matches = ".+") })
-public class QianFanAutoConfigurationIT {
-
- private static final Log logger = LogFactory.getLog(QianFanAutoConfigurationIT.class);
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.apiKey=" + System.getenv("QIANFAN_API_KEY"),
- "spring.ai.qianfan.secretKey=" + System.getenv("QIANFAN_SECRET_KEY"))
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class));
-
- @Test
- void generate() {
- this.contextRunner.run(context -> {
- QianFanChatModel client = context.getBean(QianFanChatModel.class);
- String response = client.call("Hello");
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void generateStreaming() {
- this.contextRunner.run(context -> {
- QianFanChatModel client = context.getBean(QianFanChatModel.class);
- Flux responseFlux = client.stream(new Prompt(new UserMessage("Hello")));
- String response = Objects.requireNonNull(responseFlux.collectList().block())
- .stream()
- .map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText())
- .collect(Collectors.joining());
- assertThat(response).isNotEmpty();
- logger.info("Response: " + response);
- });
- }
-
- @Test
- void embedding() {
- this.contextRunner.run(context -> {
- QianFanEmbeddingModel embeddingClient = context.getBean(QianFanEmbeddingModel.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()).isEqualTo(0);
- assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
- assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
-
- assertThat(embeddingClient.dimensions()).isEqualTo(1024);
- });
- }
-
- @Test
- void generateImage() {
- this.contextRunner.withPropertyValues("spring.ai.qianfan.image.options.size=1024x1024").run(context -> {
- QianFanImageModel imageModel = context.getBean(QianFanImageModel.class);
- ImageResponse imageResponse = imageModel.call(new ImagePrompt("forest"));
- assertThat(imageResponse.getResults()).hasSize(1);
- assertThat(imageResponse.getResult().getOutput().getUrl()).isNull();
- assertThat(imageResponse.getResult().getOutput().getB64Json()).isNotEmpty();
- logger.info("Generated image: " + imageResponse.getResult().getOutput().getB64Json());
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanPropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanPropertiesTests.java
deleted file mode 100644
index 8d976f971..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/qianfan/autoconfigure/QianFanPropertiesTests.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * 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.model.qianfan.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration;
-import org.springframework.ai.qianfan.QianFanChatModel;
-import org.springframework.ai.qianfan.QianFanEmbeddingModel;
-import org.springframework.ai.qianfan.QianFanImageModel;
-import org.springframework.ai.qianfan.api.QianFanApi;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
-import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit Tests for {@link QianFanConnectionProperties}, {@link QianFanChatProperties} and
- * {@link QianFanEmbeddingProperties}.
- *
- * @author Geng Rong
- */
-public class QianFanPropertiesTests {
-
- @Test
- public void chatProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.chat.options.model=MODEL_XYZ",
- "spring.ai.qianfan.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(QianFanChatProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isNull();
- assertThat(chatProperties.getBaseUrl()).isNull();
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void chatOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.chat.base-url=TEST_BASE_URL2",
- "spring.ai.qianfan.chat.api-key=456",
- "spring.ai.qianfan.chat.secret-key=def456",
- "spring.ai.qianfan.chat.options.model=MODEL_XYZ",
- "spring.ai.qianfan.chat.options.temperature=0.55")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(QianFanChatProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(chatProperties.getApiKey()).isEqualTo("456");
- assertThat(chatProperties.getSecretKey()).isEqualTo("def456");
- assertThat(chatProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- });
- }
-
- @Test
- public void embeddingProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(QianFanEmbeddingProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isNull();
- assertThat(embeddingProperties.getBaseUrl()).isNull();
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void embeddingOverrideConnectionProperties() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.embedding.base-url=TEST_BASE_URL2",
- "spring.ai.qianfan.embedding.api-key=456",
- "spring.ai.qianfan.embedding.secret-key=def456",
- "spring.ai.qianfan.embedding.options.model=MODEL_XYZ")
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var embeddingProperties = context.getBean(QianFanEmbeddingProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(embeddingProperties.getApiKey()).isEqualTo("456");
- assertThat(embeddingProperties.getSecretKey()).isEqualTo("def456");
- assertThat(embeddingProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- public void chatOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.api-key=API_KEY",
- "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
-
- "spring.ai.qianfan.chat.options.model=MODEL_XYZ",
- "spring.ai.qianfan.chat.options.frequencyPenalty=-1.5",
- "spring.ai.qianfan.chat.options.logitBias.myTokenId=-5",
- "spring.ai.qianfan.chat.options.maxTokens=123",
- "spring.ai.qianfan.chat.options.presencePenalty=0",
- "spring.ai.qianfan.chat.options.responseFormat.type=json",
- "spring.ai.qianfan.chat.options.stop=boza,koza",
- "spring.ai.qianfan.chat.options.temperature=0.55",
- "spring.ai.qianfan.chat.options.topP=0.56"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var chatProperties = context.getBean(QianFanChatProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
- var embeddingProperties = context.getBean(QianFanEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("SECRET_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("bge_large_zh");
-
- assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(chatProperties.getOptions().getFrequencyPenalty()).isEqualTo(-1.5);
- assertThat(chatProperties.getOptions().getMaxTokens()).isEqualTo(123);
- assertThat(chatProperties.getOptions().getPresencePenalty()).isEqualTo(0);
- assertThat(chatProperties.getOptions().getResponseFormat())
- .isEqualTo(new QianFanApi.ChatCompletionRequest.ResponseFormat("json"));
- assertThat(chatProperties.getOptions().getStop()).contains("boza", "koza");
- assertThat(chatProperties.getOptions().getTemperature()).isEqualTo(0.55);
- assertThat(chatProperties.getOptions().getTopP()).isEqualTo(0.56);
- });
- }
-
- @Test
- public void embeddingOptionsTest() {
-
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.api-key=API_KEY",
- "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
-
- "spring.ai.qianfan.embedding.options.model=MODEL_XYZ",
- "spring.ai.qianfan.embedding.options.encodingFormat=MyEncodingFormat"
- )
- // @formatter:on
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
- var embeddingProperties = context.getBean(QianFanEmbeddingProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("SECRET_KEY");
-
- assertThat(embeddingProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- });
- }
-
- @Test
- void embeddingActivation() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.embedding.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanEmbeddingModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanEmbeddingModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.embedding.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanEmbeddingProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanEmbeddingModel.class)).isNotEmpty();
- });
- }
-
- @Test
- void chatActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.chat.enabled=false")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanChatModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanChatModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.chat.enabled=true")
- .withConfiguration(AutoConfigurations.of(SpringAiRetryAutoConfiguration.class,
- RestClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanChatProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanChatModel.class)).isNotEmpty();
- });
-
- }
-
- @Test
- public void imageProperties() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.image.options.model=MODEL_XYZ",
- "spring.ai.qianfan.image.options.n=3")
- // @formatter:on
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(QianFanImageProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(imageProperties.getApiKey()).isNull();
- assertThat(imageProperties.getBaseUrl()).isNull();
-
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- });
- }
-
- @Test
- public void imageOverrideConnectionProperties() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
- "spring.ai.qianfan.api-key=abc123",
- "spring.ai.qianfan.secret-key=def123",
- "spring.ai.qianfan.image.base-url=TEST_BASE_URL2",
- "spring.ai.qianfan.image.api-key=456",
- "spring.ai.qianfan.image.secret-key=def456",
- "spring.ai.qianfan.image.options.model=MODEL_XYZ",
- "spring.ai.qianfan.image.options.n=3")
- // @formatter:on
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(QianFanImageProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getApiKey()).isEqualTo("abc123");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("def123");
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
-
- assertThat(imageProperties.getApiKey()).isEqualTo("456");
- assertThat(imageProperties.getSecretKey()).isEqualTo("def456");
- assertThat(imageProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL2");
-
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- });
- }
-
- @Test
- public void imageOptionsTest() {
- new ApplicationContextRunner().withPropertyValues(
- // @formatter:off
- "spring.ai.qianfan.api-key=API_KEY",
- "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL",
-
- "spring.ai.qianfan.image.options.n=3",
- "spring.ai.qianfan.image.options.model=MODEL_XYZ",
- "spring.ai.qianfan.image.options.size=1024x1024",
- "spring.ai.qianfan.image.options.width=1024",
- "spring.ai.qianfan.image.options.height=1024",
- "spring.ai.qianfan.image.options.style=vivid",
- "spring.ai.qianfan.image.options.user=userXYZ"
- )
- // @formatter:on
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- var imageProperties = context.getBean(QianFanImageProperties.class);
- var connectionProperties = context.getBean(QianFanConnectionProperties.class);
-
- assertThat(connectionProperties.getBaseUrl()).isEqualTo("TEST_BASE_URL");
- assertThat(connectionProperties.getApiKey()).isEqualTo("API_KEY");
- assertThat(connectionProperties.getSecretKey()).isEqualTo("SECRET_KEY");
-
- assertThat(imageProperties.getOptions().getN()).isEqualTo(3);
- assertThat(imageProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
- assertThat(imageProperties.getOptions().getSize()).isEqualTo("1024x1024");
- assertThat(imageProperties.getOptions().getWidth()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getHeight()).isEqualTo(1024);
- assertThat(imageProperties.getOptions().getStyle()).isEqualTo("vivid");
- assertThat(imageProperties.getOptions().getUser()).isEqualTo("userXYZ");
- });
- }
-
- @Test
- void imageActivation() {
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.image.enabled=false")
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanImageModel.class)).isEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL")
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanImageModel.class)).isNotEmpty();
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.qianfan.api-key=API_KEY", "spring.ai.qianfan.secret-key=SECRET_KEY",
- "spring.ai.qianfan.base-url=TEST_BASE_URL", "spring.ai.qianfan.image.enabled=true")
- .withConfiguration(
- AutoConfigurations.of(SpringAiRetryAutoConfiguration.class, RestClientAutoConfiguration.class,
- WebClientAutoConfiguration.class, QianFanAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(QianFanImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(QianFanImageModel.class)).isNotEmpty();
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiAutoConfigurationIT.java
deleted file mode 100644
index ad447cb4f..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiAutoConfigurationIT.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
-
-import org.springframework.ai.image.Image;
-import org.springframework.ai.image.ImageGeneration;
-import org.springframework.ai.image.ImageModel;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-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() {
- this.contextRunner.run(context -> {
- ImageModel imageModel = context.getBean(ImageModel.class);
- StabilityAiImageOptions imageOptions = StabilityAiImageOptions.builder()
- .stylePreset(StyleEnum.PHOTOGRAPHIC)
- .build();
-
- var instructions = """
- A light cream colored mini golden doodle.
- """;
-
- ImagePrompt imagePrompt = new ImagePrompt(instructions, imageOptions);
- ImageResponse imageResponse = imageModel.call(imagePrompt);
-
- ImageGeneration imageGeneration = imageResponse.getResult();
- Image image = imageGeneration.getOutput();
-
- assertThat(image.getB64Json()).isNotEmpty();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImagePropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImagePropertiesTests.java
deleted file mode 100644
index 74e1624f9..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/stabilityai/autoconfigure/StabilityAiImagePropertiesTests.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.model.stabilityai.autoconfigure;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.stabilityai.StabilityAiImageModel;
-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");
- });
- }
-
- @Test
- void stabilityImageActivation() {
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.stabilityai.image.api-key=API_KEY",
- "spring.ai.stabilityai.image.base-url=ENDPOINT", "spring.ai.stabilityai.image.enabled=false")
- .withConfiguration(AutoConfigurations.of(StabilityAiImageAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(StabilityAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(StabilityAiImageModel.class)).isEmpty();
-
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.stabilityai.image.api-key=API_KEY",
- "spring.ai.stabilityai.image.base-url=ENDPOINT")
- .withConfiguration(AutoConfigurations.of(StabilityAiImageAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(StabilityAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(StabilityAiImageModel.class)).isNotEmpty();
-
- });
-
- new ApplicationContextRunner()
- .withPropertyValues("spring.ai.stabilityai.image.api-key=API_KEY",
- "spring.ai.stabilityai.image.base-url=ENDPOINT", "spring.ai.stabilityai.image.enabled=true")
- .withConfiguration(AutoConfigurations.of(StabilityAiImageAutoConfiguration.class))
- .run(context -> {
- assertThat(context.getBeansOfType(StabilityAiImageProperties.class)).isNotEmpty();
- assertThat(context.getBeansOfType(StabilityAiImageModel.class)).isNotEmpty();
-
- });
-
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java
deleted file mode 100644
index f67bad99b..000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright 2023-2025 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.model.tool.autoconfigure;
-
-import java.util.function.Function;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.model.tool.DefaultToolCallingManager;
-import org.springframework.ai.model.tool.ToolCallingManager;
-import org.springframework.ai.tool.StaticToolCallbackProvider;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.ToolCallbackProvider;
-import org.springframework.ai.tool.annotation.Tool;
-import org.springframework.ai.tool.definition.ToolDefinition;
-import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor;
-import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor;
-import org.springframework.ai.tool.function.FunctionToolCallback;
-import org.springframework.ai.tool.method.MethodToolCallback;
-import org.springframework.ai.tool.method.MethodToolCallbackProvider;
-import org.springframework.ai.tool.resolution.DelegatingToolCallbackResolver;
-import org.springframework.ai.tool.resolution.ToolCallbackResolver;
-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.context.annotation.Description;
-import org.springframework.util.ReflectionUtils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Unit tests for {@link ToolCallingAutoConfiguration}.
- *
- * @author Thomas Vitale
- * @author Christian Tzolov
- */
-class ToolCallingAutoConfigurationTests {
-
- @Test
- void beansAreCreated() {
- new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(ToolCallingAutoConfiguration.class))
- .run(context -> {
- var toolCallbackResolver = context.getBean(ToolCallbackResolver.class);
- assertThat(toolCallbackResolver).isInstanceOf(DelegatingToolCallbackResolver.class);
-
- var toolExecutionExceptionProcessor = context.getBean(ToolExecutionExceptionProcessor.class);
- assertThat(toolExecutionExceptionProcessor).isInstanceOf(DefaultToolExecutionExceptionProcessor.class);
-
- var toolCallingManager = context.getBean(ToolCallingManager.class);
- assertThat(toolCallingManager).isInstanceOf(DefaultToolCallingManager.class);
- });
- }
-
- @Test
- void resolveMultipleFuncitonAndToolCallbacks() {
- new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(ToolCallingAutoConfiguration.class))
- .withUserConfiguration(Config.class)
- .run(context -> {
- var toolCallbackResolver = context.getBean(ToolCallbackResolver.class);
- assertThat(toolCallbackResolver).isInstanceOf(DelegatingToolCallbackResolver.class);
-
- assertThat(toolCallbackResolver.resolve("getForecast")).isNotNull();
- assertThat(toolCallbackResolver.resolve("getForecast").getName()).isEqualTo("getForecast");
-
- assertThat(toolCallbackResolver.resolve("getAlert")).isNotNull();
- assertThat(toolCallbackResolver.resolve("getAlert").getName()).isEqualTo("getAlert");
-
- assertThat(toolCallbackResolver.resolve("weatherFunction1")).isNotNull();
- assertThat(toolCallbackResolver.resolve("weatherFunction1").getName()).isEqualTo("weatherFunction1");
-
- assertThat(toolCallbackResolver.resolve("getCurrentWeather3")).isNotNull();
- assertThat(toolCallbackResolver.resolve("getCurrentWeather3").getName())
- .isEqualTo("getCurrentWeather3");
-
- assertThat(toolCallbackResolver.resolve("getCurrentWeather4")).isNotNull();
- assertThat(toolCallbackResolver.resolve("getCurrentWeather4").getName())
- .isEqualTo("getCurrentWeather4");
-
- assertThat(toolCallbackResolver.resolve("getCurrentWeather5")).isNotNull();
- assertThat(toolCallbackResolver.resolve("getCurrentWeather5").getName())
- .isEqualTo("getCurrentWeather5");
- });
- }
-
- static class WeatherService {
-
- @Tool(description = "Get the weather in location. Return temperature in 36°F or 36°C format.")
- public String getForecast(String location) {
- return "30";
- }
-
- @Tool(description = "Get the weather in location. Return temperature in 36°F or 36°C format.")
- public String getForecast2(String location) {
- return "30";
- }
-
- public String getAlert(String usState) {
- return "Alert";
- }
-
- }
-
- @Configuration
- static class Config {
-
- // Note: Currently we do not have ToolCallbackResolver implementation that can
- // resolve the ToolCallback from the Tool annotation.
- // Therefore we need to provide the ToolCallback instances explicitly using the
- // ToolCallbacks.from(...) utility method.
- @Bean
- public ToolCallbackProvider toolCallbacks() {
- return MethodToolCallbackProvider.builder().toolObjects(new WeatherService()).build();
- }
-
- public record Request(String location) {
- }
-
- public record Response(String temperature) {
- }
-
- @Bean
- @Description("Get the weather in location. Return temperature in 36°F or 36°C format.")
- public Function