diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml index e3a171e2d07..ecb49ce4c23 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/pom.xml @@ -39,7 +39,6 @@ org.springframework.ai spring-ai-autoconfigure-retry ${project.parent.version} - true diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java index dcdd8c2fbf7..a3b0904811f 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaApiAutoConfiguration.java @@ -17,12 +17,15 @@ package org.springframework.ai.model.ollama.autoconfigure; import org.springframework.ai.ollama.api.OllamaApi; +import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; 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.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; import org.springframework.web.reactive.function.client.WebClient; @@ -38,6 +41,7 @@ @AutoConfiguration @ConditionalOnClass(OllamaApi.class) @EnableConfigurationProperties(OllamaConnectionProperties.class) +@ImportAutoConfiguration(classes = { SpringAiRetryAutoConfiguration.class }) public class OllamaApiAutoConfiguration { @Bean @@ -50,11 +54,12 @@ public PropertiesOllamaConnectionDetails ollamaConnectionDetails(OllamaConnectio @ConditionalOnMissingBean public OllamaApi ollamaApi(OllamaConnectionDetails connectionDetails, ObjectProvider restClientBuilderProvider, - ObjectProvider webClientBuilderProvider) { + ObjectProvider webClientBuilderProvider, ResponseErrorHandler responseErrorHandler) { return OllamaApi.builder() .baseUrl(connectionDetails.getBaseUrl()) .restClientBuilder(restClientBuilderProvider.getIfAvailable(RestClient::builder)) .webClientBuilder(webClientBuilderProvider.getIfAvailable(WebClient::builder)) + .responseErrorHandler(responseErrorHandler) .build(); } diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java index 98518ba4568..e8cbc25a31f 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/main/java/org/springframework/ai/model/ollama/autoconfigure/OllamaChatAutoConfiguration.java @@ -39,6 +39,7 @@ import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.retry.support.RetryTemplate; /** * {@link AutoConfiguration Auto-configuration} for Ollama Chat model. @@ -47,6 +48,7 @@ * @author Eddú Meléndez * @author Thomas Vitale * @author Ilayaperumal Gopinathan + * @author Jonghoon Park * @since 0.8.0 */ @AutoConfiguration(after = { RestClientAutoConfiguration.class, ToolCallingAutoConfiguration.class }) @@ -64,7 +66,8 @@ public OllamaChatModel ollamaChatModel(OllamaApi ollamaApi, OllamaChatProperties OllamaInitializationProperties initProperties, ToolCallingManager toolCallingManager, ObjectProvider observationRegistry, ObjectProvider observationConvention, - ObjectProvider ollamaToolExecutionEligibilityPredicate) { + ObjectProvider ollamaToolExecutionEligibilityPredicate, + RetryTemplate retryTemplate) { var chatModelPullStrategy = initProperties.getChat().isInclude() ? initProperties.getPullModelStrategy() : PullModelStrategy.NEVER; @@ -78,6 +81,7 @@ public OllamaChatModel ollamaChatModel(OllamaApi ollamaApi, OllamaChatProperties .modelManagementOptions( new ModelManagementOptions(chatModelPullStrategy, initProperties.getChat().getAdditionalModels(), initProperties.getTimeout(), initProperties.getMaxRetries())) + .retryTemplate(retryTemplate) .build(); observationConvention.ifAvailable(chatModel::setObservationConvention); diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaEmbeddingModelObservationIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaEmbeddingModelObservationIT.java index baaa9ab21d0..a94dbbe6312 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaEmbeddingModelObservationIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaEmbeddingModelObservationIT.java @@ -97,12 +97,12 @@ public TestObservationRegistry observationRegistry() { } @Bean - public OllamaApi openAiApi() { + public OllamaApi ollamaApi() { return initializeOllama(MODEL); } @Bean - public OllamaEmbeddingModel openAiEmbeddingModel(OllamaApi ollamaApi, + public OllamaEmbeddingModel ollamaEmbeddingModel(OllamaApi ollamaApi, TestObservationRegistry observationRegistry) { return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).observationRegistry(observationRegistry).build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java deleted file mode 100644 index 5675e4c4a1a..00000000000 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/ollama/OllamaAutoConfiguration.java +++ /dev/null @@ -1,153 +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.autoconfigure.ollama; - -import io.micrometer.observation.ObservationRegistry; - -import org.springframework.ai.autoconfigure.chat.model.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.retry.support.RetryTemplate; -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, RetryTemplate retryTemplate) { - 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())) - .retryTemplate(retryTemplate) - .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(); - } - - } - -}