diff --git a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java index 36d212c8d..27cdda308 100644 --- a/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java +++ b/auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.ai.retry.autoconfigure; import java.io.IOException; +import java.net.URI; import java.nio.charset.StandardCharsets; import org.slf4j.Logger; @@ -30,6 +31,7 @@ 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.HttpMethod; import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.NonNull; import org.springframework.retry.RetryCallback; @@ -87,7 +89,8 @@ public class SpringAiRetryAutoConfiguration { } @Override - public void handleError(@NonNull ClientHttpResponse response) throws IOException { + public void handleError(URI uri, HttpMethod httpMethod, @NonNull ClientHttpResponse response) + throws IOException { if (!response.getStatusCode().isError()) { return; } diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java index adcb897ad..24ce1cd14 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/api/AnthropicApi.java @@ -162,7 +162,7 @@ public final class AnthropicApi { return this.restClient.post() .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) + .headers(headers -> headers.addAll(HttpHeaders.readOnlyHttpHeaders(additionalHttpHeader))) .body(chatRequest) .retrieve() .toEntity(ChatCompletionResponse.class); @@ -198,7 +198,7 @@ public final class AnthropicApi { return this.webClient.post() .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) + .headers(headers -> headers.addAll(HttpHeaders.readOnlyHttpHeaders(additionalHttpHeader))) .body(Mono.just(chatRequest), ChatCompletionRequest.class) .retrieve() .bodyToFlux(String.class) diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java index f565c2ba2..e215f36b2 100644 --- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java +++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/api/DeepSeekApi.java @@ -94,7 +94,7 @@ public class DeepSeekApi { Consumer finalHeaders = h -> { h.setBearerAuth(apiKey.getValue()); h.setContentType(MediaType.APPLICATION_JSON); - h.addAll(headers); + h.addAll(HttpHeaders.readOnlyHttpHeaders(headers)); }; this.restClient = restClientBuilder.baseUrl(baseUrl) .defaultHeaders(finalHeaders) @@ -153,7 +153,7 @@ public class DeepSeekApi { return this.webClient.post() .uri(this.getEndpoint(chatRequest)) - .headers(headers -> headers.addAll(additionalHttpHeader)) + .headers(headers -> headers.addAll(HttpHeaders.readOnlyHttpHeaders(additionalHttpHeader))) .body(Mono.just(chatRequest), ChatCompletionRequest.class) .retrieve() .bodyToFlux(String.class) diff --git a/models/spring-ai-huggingface/pom.xml b/models/spring-ai-huggingface/pom.xml index 9412e970d..1d29d7373 100644 --- a/models/spring-ai-huggingface/pom.xml +++ b/models/spring-ai-huggingface/pom.xml @@ -51,7 +51,7 @@ io.swagger.core.v3 swagger-annotations - 2.2.15 + 2.2.32 diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java index 2b11927f9..917e966e5 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java @@ -133,7 +133,7 @@ public class OpenAiApi { } h.setContentType(MediaType.APPLICATION_JSON); - h.addAll(headers); + h.addAll(HttpHeaders.readOnlyHttpHeaders(headers)); }; this.restClient = restClientBuilder.clone() .baseUrl(baseUrl) @@ -181,7 +181,7 @@ public class OpenAiApi { return this.restClient.post() .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) + .headers(headers -> headers.addAll(HttpHeaders.readOnlyHttpHeaders(additionalHttpHeader))) .body(chatRequest) .retrieve() .toEntity(ChatCompletion.class); @@ -215,7 +215,7 @@ public class OpenAiApi { return this.webClient.post() .uri(this.completionsPath) - .headers(headers -> headers.addAll(additionalHttpHeader)) + .headers(headers -> headers.addAll(HttpHeaders.readOnlyHttpHeaders(additionalHttpHeader))) .body(Mono.just(chatRequest), ChatCompletionRequest.class) .retrieve() .bodyToFlux(String.class) diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java index 95604f300..ecf60d11b 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiAudioApi.java @@ -74,7 +74,7 @@ public class OpenAiAudioApi { if (!(apiKey instanceof NoopApiKey)) { h.setBearerAuth(apiKey.getValue()); } - h.addAll(headers); + h.addAll(HttpHeaders.readOnlyHttpHeaders(headers)); // h.setContentType(MediaType.APPLICATION_JSON); }; diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java index bd32d42d9..f013a0b11 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiImageApi.java @@ -27,6 +27,7 @@ import org.springframework.ai.model.NoopApiKey; import org.springframework.ai.model.SimpleApiKey; import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; @@ -68,7 +69,7 @@ public class OpenAiImageApi { h.setBearerAuth(apiKey.getValue()); } h.setContentType(MediaType.APPLICATION_JSON); - h.addAll(headers); + h.addAll(HttpHeaders.readOnlyHttpHeaders(headers)); }) .defaultStatusHandler(responseErrorHandler) .build(); diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java index b9a6578b6..978d8388c 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiModerationApi.java @@ -27,6 +27,7 @@ import org.springframework.ai.model.NoopApiKey; import org.springframework.ai.model.SimpleApiKey; import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; @@ -69,7 +70,7 @@ public class OpenAiModerationApi { h.setBearerAuth(apiKey.getValue()); } h.setContentType(MediaType.APPLICATION_JSON); - h.addAll(headers); + h.addAll(HttpHeaders.readOnlyHttpHeaders(headers)); }).defaultStatusHandler(responseErrorHandler).build(); } diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java index 7a4d34475..d4fd2b979 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/metadata/support/OpenAiResponseHeaderExtractor.java @@ -71,7 +71,7 @@ public final class OpenAiResponseHeaderExtractor { private static Duration getHeaderAsDuration(ResponseEntity response, String headerName) { var headers = response.getHeaders(); - if (headers.containsKey(headerName)) { + if (headers.containsHeader(headerName)) { var values = headers.get(headerName); if (!CollectionUtils.isEmpty(values)) { return DurationFormatter.TIME_UNIT.parse(values.get(0)); @@ -82,7 +82,7 @@ public final class OpenAiResponseHeaderExtractor { private static Long getHeaderAsLong(ResponseEntity response, String headerName) { var headers = response.getHeaders(); - if (headers.containsKey(headerName)) { + if (headers.containsHeader(headerName)) { var values = headers.get(headerName); if (!CollectionUtils.isEmpty(values)) { return parseLong(headerName, values.get(0)); diff --git a/pom.xml b/pom.xml index ca54365aa..0c49bb839 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ models/spring-ai-azure-openai models/spring-ai-bedrock models/spring-ai-bedrock-converse - models/spring-ai-huggingface + models/spring-ai-minimax models/spring-ai-mistral-ai models/spring-ai-oci-genai @@ -253,12 +253,12 @@ ${java.version} - 3.4.5 + 4.0.0-SNAPSHOT 4.3.4 1.0.0-beta.16 1.1.0 4.37.0 - 1.9.25 + 2.1.0 2.31.26 diff --git a/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt b/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt index 40a4c6ffd..fbebcfae2 100644 --- a/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt +++ b/spring-ai-client-chat/src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt @@ -25,8 +25,8 @@ import org.springframework.core.ParameterizedTypeReference * @author Josh Long */ -inline fun ChatClient.CallResponseSpec.entity(): T = +inline fun ChatClient.CallResponseSpec.entity(): T = entity(object : ParameterizedTypeReference() {}) as T -inline fun ChatClient.CallResponseSpec.responseEntity(): ResponseEntity = - responseEntity(object : ParameterizedTypeReference() {}) +inline fun ChatClient.CallResponseSpec.responseEntity(): ResponseEntity = + responseEntity(object : ParameterizedTypeReference() {}) diff --git a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt b/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt index b7671a89b..a434f319d 100644 --- a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt +++ b/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/TypeResolverHelperKotlinIT.kt @@ -39,7 +39,7 @@ class TypeResolverHelperKotlinIT { val functionType = TypeResolverHelper.resolveBeanType(this.applicationContext, beanName); val functionInputClass = TypeResolverHelper.getFunctionArgumentType(functionType, 0).rawClass; assertThat(functionInputClass).isNotNull(); - assertThat(functionInputClass.typeName).isEqualTo(WeatherRequest::class.java.getName()); + assertThat(functionInputClass!!.typeName).isEqualTo(WeatherRequest::class.java.name) } class Outer { diff --git a/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java b/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java index 960382d5c..836dd01a0 100644 --- a/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java +++ b/spring-ai-retry/src/main/java/org/springframework/ai/retry/RetryUtils.java @@ -17,12 +17,14 @@ package org.springframework.ai.retry; import java.io.IOException; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.NonNull; import org.springframework.retry.RetryCallback; @@ -49,7 +51,7 @@ public abstract class RetryUtils { } @Override - public void handleError(@NonNull ClientHttpResponse response) throws IOException { + public void handleError(URI uri, HttpMethod method, @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); diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java index f09194632..12ca89e73 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStore.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.stream.Collectors; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.mapping.DenseVectorSimilarity; import co.elastic.clients.elasticsearch.core.BulkRequest; import co.elastic.clients.elasticsearch.core.BulkResponse; import co.elastic.clients.elasticsearch.core.SearchResponse; @@ -331,7 +332,8 @@ public class ElasticsearchVectorStore extends AbstractObservationVectorStore imp this.elasticsearchClient.indices() .create(cr -> cr.index(this.options.getIndexName()) .mappings(map -> map.properties(this.options.getEmbeddingFieldName(), - p -> p.denseVector(dv -> dv.similarity(this.options.getSimilarity().toString()) + p -> p.denseVector(dv -> dv + .similarity(DenseVectorSimilarity.valueOf(this.options.getSimilarity().toString())) .dims(this.options.getDimensions()))))); } catch (IOException e) {