Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.azure.openai.metadata.AzureOpenAiImageGenerationMetadata;
Expand All @@ -22,6 +22,7 @@
import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.image.ImageResponseMetadata;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.util.JacksonUtils;
import org.springframework.util.Assert;

import java.util.List;
Expand All @@ -47,6 +48,8 @@ public class AzureOpenAiImageModel implements ImageModel {

private final AzureOpenAiImageOptions defaultOptions;

private final ObjectMapper objectMapper;

public AzureOpenAiImageModel(OpenAIClient openAIClient) {
this(openAIClient, AzureOpenAiImageOptions.builder().withDeploymentName(DEFAULT_DEPLOYMENT_NAME).build());
}
Expand All @@ -56,6 +59,11 @@ public AzureOpenAiImageModel(OpenAIClient microsoftOpenAiClient, AzureOpenAiImag
Assert.notNull(options, "AzureOpenAiChatOptions must not be null");
this.openAIClient = microsoftOpenAiClient;
this.defaultOptions = options;
this.objectMapper = JsonMapper.builder()
.addModules(JacksonUtils.instantiateAvailableModules())
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.build();
}

public AzureOpenAiImageOptions getDefaultOptions() {
Expand Down Expand Up @@ -88,11 +96,8 @@ public ImageResponse call(ImagePrompt imagePrompt) {
}

private String toPrettyJson(Object object) {
ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.registerModule(new JavaTimeModule());
try {
return objectMapper.writeValueAsString(object);
return this.objectMapper.writeValueAsString(object);
}
catch (JsonProcessingException e) {
return "JsonProcessingException:" + e + " [" + object.toString() + "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;

import org.springframework.ai.util.JacksonUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
Expand Down Expand Up @@ -116,6 +118,8 @@ public class VertexAiPaLm2Api {

private final String embeddingModel;

private final ObjectMapper objectMapper;

/**
* Create a new chat completion api.
* @param apiKey vertex apiKey.
Expand All @@ -138,6 +142,7 @@ public VertexAiPaLm2Api(String baseUrl, String apiKey, String model, String embe
this.chatModel = model;
this.embeddingModel = embeddingModel;
this.apiKey = apiKey;
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();

Consumer<HttpHeaders> jsonContentHeaders = headers -> {
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
Expand All @@ -154,7 +159,7 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getStatusCode().isError()) {
throw new RuntimeException(String.format("%s - %s", response.getStatusCode().value(),
new ObjectMapper().readValue(response.getBody(), ResponseError.class)));
objectMapper.readValue(response.getBody(), ResponseError.class)));
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
import java.lang.reflect.Type;
import java.util.Objects;

import com.fasterxml.jackson.databind.json.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.ai.util.JacksonUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.lang.NonNull;

Expand All @@ -34,7 +37,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
Expand Down Expand Up @@ -65,12 +67,10 @@ public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
/**
* The target class type reference to which the output will be converted.
*/
@SuppressWarnings({ "FieldMayBeFinal" })
private TypeReference<T> typeRef;
private final TypeReference<T> typeRef;

/** The object mapper used for deserialization and other JSON operations. */
@SuppressWarnings("FieldMayBeFinal")
private ObjectMapper objectMapper;
private final ObjectMapper objectMapper;

/**
* Constructor to initialize with the target type's class.
Expand Down Expand Up @@ -149,7 +149,7 @@ private void generateSchema() {
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonNode = generator.generateSchema(this.typeRef.getType());
ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter()
ObjectWriter objectWriter = this.objectMapper.writer(new DefaultPrettyPrinter()
.withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator())));
try {
this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
Expand Down Expand Up @@ -201,10 +201,10 @@ public T convert(@NonNull String text) {
* @return Configured object mapper.
*/
protected ObjectMapper getObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JavaTimeModule());
return mapper;
return JsonMapper.builder()
.addModules(JacksonUtils.instantiateAvailableModules())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.model.function.FunctionCallbackContext.SchemaType;
import org.springframework.ai.util.JacksonUtils;
import org.springframework.util.Assert;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.json.JsonMapper;

/**
* Note that the underlying function is responsible for converting the output into format
Expand Down Expand Up @@ -90,10 +91,7 @@ public Builder(Function<I, O> function) {

private String inputTypeSchema;

private ObjectMapper objectMapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.registerModule(new JavaTimeModule());
private ObjectMapper objectMapper;

public Builder<I, O> withName(String name) {
Assert.hasText(name, "Name must not be empty");
Expand Down Expand Up @@ -142,7 +140,14 @@ public FunctionCallbackWrapper<I, O> build() {
Assert.hasText(this.name, "Name must not be empty");
Assert.hasText(this.description, "Description must not be empty");
Assert.notNull(this.responseConverter, "ResponseConverter must not be null");
Assert.notNull(this.objectMapper, "ObjectMapper must not be null");

if (this.objectMapper == null) {
this.objectMapper = JsonMapper.builder()
.addModules(JacksonUtils.instantiateAvailableModules())
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.build();
}

if (this.inputType == null) {
if (this.function != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
Expand All @@ -30,6 +31,8 @@
import java.util.Map;
import java.util.Objects;

import org.springframework.ai.util.JacksonUtils;

import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON;
import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12;

Expand Down Expand Up @@ -91,7 +94,7 @@ private void generateSchema() {
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonNode = generator.generateSchema(this.clazz);
ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter()
ObjectWriter objectWriter = this.objectMapper.writer(new DefaultPrettyPrinter()
.withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator())));
try {
this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
Expand Down Expand Up @@ -142,9 +145,10 @@ private String jsonSchemaToInstance(String text) {
* @return Configured object mapper.
*/
protected ObjectMapper getObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
return JsonMapper.builder()
.addModules(JacksonUtils.instantiateAvailableModules())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.springframework.ai.util;

import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.databind.Module;

import org.springframework.beans.BeanUtils;
import org.springframework.core.KotlinDetector;
import org.springframework.util.ClassUtils;

/**
* Utility methods for Jackson.
*
* @author Sebastien Deleuze
*/
public abstract class JacksonUtils {

/**
* Instantiate well-known Jackson modules available in the classpath.
* <p>
* Supports the follow-modules: <code>Jdk8Module</code>, <code>JavaTimeModule</code>,
* <code>ParameterNamesModule</code> and <code>KotlinModule</code>.
* @return The list of instantiated modules.
*/
@SuppressWarnings("unchecked")
public static List<Module> instantiateAvailableModules() {
List<Module> modules = new ArrayList<>();
try {
Class<? extends com.fasterxml.jackson.databind.Module> jdk8ModuleClass = (Class<? extends Module>) ClassUtils
.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", null);
com.fasterxml.jackson.databind.Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
modules.add(jdk8Module);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jdk8 not available
}

try {
Class<? extends com.fasterxml.jackson.databind.Module> javaTimeModuleClass = (Class<? extends Module>) ClassUtils
.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", null);
com.fasterxml.jackson.databind.Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
modules.add(javaTimeModule);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available
}

try {
Class<? extends com.fasterxml.jackson.databind.Module> parameterNamesModuleClass = (Class<? extends Module>) ClassUtils
.forName("com.fasterxml.jackson.module.paramnames.ParameterNamesModule", null);
com.fasterxml.jackson.databind.Module parameterNamesModule = BeanUtils
.instantiateClass(parameterNamesModuleClass);
modules.add(parameterNamesModule);
}
catch (ClassNotFoundException ex) {
// jackson-module-parameter-names not available
}

// Kotlin present?
if (KotlinDetector.isKotlinPresent()) {
try {
Class<? extends com.fasterxml.jackson.databind.Module> kotlinModuleClass = (Class<? extends Module>) ClassUtils
.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", null);
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
modules.add(kotlinModule);
}
catch (ClassNotFoundException ex) {
// jackson-module-kotlin not available
}
}
return modules;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import com.fasterxml.jackson.databind.json.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
import org.springframework.ai.util.JacksonUtils;
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
Expand Down Expand Up @@ -69,6 +71,8 @@ public class SimpleVectorStore extends AbstractObservationVectorStore {

private static final Logger logger = LoggerFactory.getLogger(SimpleVectorStore.class);

private final ObjectMapper objectMapper;

protected Map<String, Document> store = new ConcurrentHashMap<>();

protected EmbeddingModel embeddingModel;
Expand All @@ -84,6 +88,7 @@ public SimpleVectorStore(EmbeddingModel embeddingModel, ObservationRegistry obse

Objects.requireNonNull(embeddingModel, "EmbeddingModel must not be null");
this.embeddingModel = embeddingModel;
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
}

@Override
Expand Down Expand Up @@ -172,9 +177,8 @@ public void save(File file) {
public void load(File file) {
TypeReference<HashMap<String, Document>> typeRef = new TypeReference<>() {
};
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, Document> deserializedMap = objectMapper.readValue(file, typeRef);
Map<String, Document> deserializedMap = this.objectMapper.readValue(file, typeRef);
this.store = deserializedMap;
}
catch (IOException ex) {
Expand All @@ -189,9 +193,8 @@ public void load(File file) {
public void load(Resource resource) {
TypeReference<HashMap<String, Document>> typeRef = new TypeReference<>() {
};
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, Document> deserializedMap = objectMapper.readValue(resource.getInputStream(), typeRef);
Map<String, Document> deserializedMap = this.objectMapper.readValue(resource.getInputStream(), typeRef);
this.store = deserializedMap;
}
catch (IOException ex) {
Expand All @@ -200,8 +203,7 @@ public void load(Resource resource) {
}

private String getVectorDbAsJson() {
ObjectMapper objectMapper = new ObjectMapper();
ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
ObjectWriter objectWriter = this.objectMapper.writerWithDefaultPrettyPrinter();
String json;
try {
json = objectWriter.writeValueAsString(this.store);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ public class BedrockAnthropicChatAutoConfiguration {
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
public AnthropicChatBedrockApi anthropicApi(AwsCredentialsProvider credentialsProvider,
AwsRegionProvider regionProvider, BedrockAnthropicChatProperties properties,
BedrockAwsConnectionProperties awsProperties) {
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
return new AnthropicChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
new ObjectMapper(), awsProperties.getTimeout());
objectMapper, awsProperties.getTimeout());
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ public class BedrockAnthropic3ChatAutoConfiguration {
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
public Anthropic3ChatBedrockApi anthropic3Api(AwsCredentialsProvider credentialsProvider,
AwsRegionProvider regionProvider, BedrockAnthropic3ChatProperties properties,
BedrockAwsConnectionProperties awsProperties) {
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
return new Anthropic3ChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
new ObjectMapper(), awsProperties.getTimeout());
objectMapper, awsProperties.getTimeout());
}

@Bean
Expand Down
Loading