diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java index f5680b03e7c..93309494dec 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/BeanOutputConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * 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. @@ -21,6 +21,7 @@ import java.util.Objects; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -41,7 +42,7 @@ import org.springframework.ai.util.JacksonUtils; import org.springframework.core.KotlinDetector; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import static org.springframework.ai.util.LoggingMarkers.SENSITIVE_DATA_MARKER; @@ -105,7 +106,8 @@ public BeanOutputConverter(Class clazz, ObjectMapper objectMapper) { * @param objectMapper Custom object mapper for JSON operations. * @param textCleaner Custom text cleaner for preprocessing responses. */ - public BeanOutputConverter(Class clazz, ObjectMapper objectMapper, ResponseTextCleaner textCleaner) { + public BeanOutputConverter(Class clazz, @Nullable ObjectMapper objectMapper, + @Nullable ResponseTextCleaner textCleaner) { this(ParameterizedTypeReference.forType(clazz), objectMapper, textCleaner); } @@ -135,8 +137,8 @@ public BeanOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper o * @param objectMapper Custom object mapper for JSON operations. * @param textCleaner Custom text cleaner for preprocessing responses. */ - public BeanOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper objectMapper, - ResponseTextCleaner textCleaner) { + public BeanOutputConverter(ParameterizedTypeReference typeRef, @Nullable ObjectMapper objectMapper, + @Nullable ResponseTextCleaner textCleaner) { this(typeRef.getType(), objectMapper, textCleaner); } @@ -148,7 +150,8 @@ public BeanOutputConverter(ParameterizedTypeReference typeRef, ObjectMapper o * @param objectMapper Custom object mapper for JSON operations. endings. * @param textCleaner Custom text cleaner for preprocessing responses. */ - private BeanOutputConverter(Type type, ObjectMapper objectMapper, ResponseTextCleaner textCleaner) { + private BeanOutputConverter(Type type, @Nullable ObjectMapper objectMapper, + @Nullable ResponseTextCleaner textCleaner) { Objects.requireNonNull(type, "Type cannot be null;"); this.type = type; this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper(); @@ -217,14 +220,13 @@ private void generateSchema() { * @param text The LLM output in string format. * @return The parsed output in the desired target type. */ - @SuppressWarnings("unchecked") @Override - public T convert(@NonNull String text) { + public T convert(String text) { try { // Clean the text using the configured text cleaner text = this.textCleaner.clean(text); - return (T) this.objectMapper.readValue(text, this.objectMapper.constructType(this.type)); + return this.objectMapper.readValue(text, this.objectMapper.constructType(this.type)); } catch (JsonProcessingException e) { logger.error(SENSITIVE_DATA_MARKER, @@ -272,7 +274,7 @@ public String getJsonSchema() { public Map getJsonSchemaMap() { try { - return this.objectMapper.readValue(this.jsonSchema, Map.class); + return this.objectMapper.readValue(this.jsonSchema, new MapTypeReference()); } catch (JsonProcessingException ex) { logger.error("Could not parse the JSON Schema to a Map object", ex); @@ -280,4 +282,8 @@ public Map getJsonSchemaMap() { } } + private static final class MapTypeReference extends TypeReference> { + + } + } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/CompositeResponseTextCleaner.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/CompositeResponseTextCleaner.java index d1def873f82..908aacce658 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/CompositeResponseTextCleaner.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/CompositeResponseTextCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/MarkdownCodeBlockCleaner.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/MarkdownCodeBlockCleaner.java index cd81634fc55..99e2cbed688 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/MarkdownCodeBlockCleaner.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/MarkdownCodeBlockCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/ResponseTextCleaner.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/ResponseTextCleaner.java index 866b495b4f4..fb33e0cd136 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/ResponseTextCleaner.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/ResponseTextCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/ThinkingTagCleaner.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/ThinkingTagCleaner.java index 11892b48151..471c8d62897 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/ThinkingTagCleaner.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/ThinkingTagCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. diff --git a/spring-ai-model/src/main/java/org/springframework/ai/converter/WhitespaceCleaner.java b/spring-ai-model/src/main/java/org/springframework/ai/converter/WhitespaceCleaner.java index 1c52a6c0421..a9db98567c5 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/converter/WhitespaceCleaner.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/converter/WhitespaceCleaner.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. diff --git a/spring-ai-model/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java b/spring-ai-model/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java index 9066aa2c814..f20037bf12c 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/converter/BeanOutputConverterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * 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. @@ -33,9 +33,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.LoggerFactory; import org.springframework.ai.util.TextBlockAssertion; @@ -52,17 +49,12 @@ * @author Soby Chacko * @author Konstantin Pavlov */ -@ExtendWith(MockitoExtension.class) class BeanOutputConverterTest { private ListAppender logAppender; - @Mock - private ObjectMapper objectMapperMock; - @BeforeEach void beforeEach() { - var logger = (Logger) LoggerFactory.getLogger(BeanOutputConverter.class); this.logAppender = new ListAppender<>(); @@ -83,19 +75,15 @@ static class TestClass { private String someString; - @SuppressWarnings("unused") TestClass() { } - TestClass(String someString) { - this.someString = someString; - } - String getSomeString() { return this.someString; } - public void setSomeString(String someString) { + @SuppressWarnings("unused") + void setSomeString(String someString) { this.someString = someString; } @@ -105,19 +93,15 @@ static class TestClassWithDateProperty { private LocalDate someString; - @SuppressWarnings("unused") TestClassWithDateProperty() { } - TestClassWithDateProperty(LocalDate someString) { - this.someString = someString; - } - LocalDate getSomeString() { return this.someString; } - public void setSomeString(LocalDate someString) { + @SuppressWarnings("unused") + void setSomeString(LocalDate someString) { this.someString = someString; } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/converter/CompositeResponseTextCleanerTest.java b/spring-ai-model/src/test/java/org/springframework/ai/converter/CompositeResponseTextCleanerTest.java index 7cf0ebc54c1..727d8995935 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/converter/CompositeResponseTextCleanerTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/converter/CompositeResponseTextCleanerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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. @@ -41,7 +41,7 @@ void shouldApplyCleanersInOrder() { @Test void shouldWorkWithSingleCleaner() { - var cleaner = new CompositeResponseTextCleaner(text -> text.trim()); + var cleaner = new CompositeResponseTextCleaner(String::trim); String result = cleaner.clean(" content "); assertThat(result).isEqualTo("content"); } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/converter/ThinkingTagCleanerTest.java b/spring-ai-model/src/test/java/org/springframework/ai/converter/ThinkingTagCleanerTest.java index a8d596f337f..5495bc005aa 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/converter/ThinkingTagCleanerTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/converter/ThinkingTagCleanerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2025-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.