Refine Jackson ObjectMapper handling
ObjectMapper instantiation is costly, so unless its usage is one-shot, it is better to create a reusable instance for upcoming usage. Also, before this commit, serialization of most Kotlin classes was not supported due to the lack of proper Jackson KotlinModule detection. This commit: - Avoids per invocation ObjectMapper instantiation when relevant - Automatically detects and enables well-known Jackson modules including the Kotlin one - Removes org.springframework.ai.vectorstore.JsonUtils which looks not needed anymore More optimizations are possible like reusing more ObjectMapper instances, but this could introduce more breaking changes so this commit intends to be a good first step. Kotlin tests will be provided in a follow-up commit. Additional changes: - Update ModelOptionsUtils to use JacksonUtils.instantiateAvailableModules() - Add missing license headers - Add missing author Javadoc comments
This commit is contained in:
committed by
Christian Tzolov
parent
9f6ca77bbc
commit
f21b8a42b5
@@ -10,7 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
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;
|
||||
@@ -22,6 +22,7 @@ import org.springframework.ai.image.ImagePrompt;
|
||||
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;
|
||||
@@ -33,6 +34,7 @@ import static java.lang.String.format;
|
||||
* {@link OpenAIClient}.
|
||||
*
|
||||
* @author Benoit Moussaud
|
||||
* @author Sebastien Deleuze
|
||||
* @see ImageModel
|
||||
* @see com.azure.ai.openai.OpenAIClient
|
||||
* @since 1.0.0
|
||||
@@ -47,6 +49,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());
|
||||
}
|
||||
@@ -56,6 +60,11 @@ public class AzureOpenAiImageModel implements ImageModel {
|
||||
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() {
|
||||
@@ -88,11 +97,8 @@ public class AzureOpenAiImageModel implements ImageModel {
|
||||
}
|
||||
|
||||
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() + "]";
|
||||
|
||||
@@ -25,7 +25,9 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
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;
|
||||
@@ -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.
|
||||
@@ -138,6 +142,7 @@ public class VertexAiPaLm2Api {
|
||||
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));
|
||||
@@ -154,7 +159,7 @@ public class VertexAiPaLm2Api {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,8 +21,11 @@ import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_
|
||||
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;
|
||||
|
||||
@@ -34,7 +37,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
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;
|
||||
@@ -54,6 +56,7 @@ import com.github.victools.jsonschema.module.jackson.JacksonOption;
|
||||
* @author Sebastian Ullrich
|
||||
* @author Kirk Lund
|
||||
* @author Josh Long
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
|
||||
@@ -65,12 +68,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.
|
||||
@@ -149,7 +150,7 @@ public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
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);
|
||||
@@ -201,10 +202,10 @@ public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ai.util.JacksonUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -33,9 +40,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.github.victools.jsonschema.generator.Option;
|
||||
import com.github.victools.jsonschema.generator.OptionPreset;
|
||||
import com.github.victools.jsonschema.generator.SchemaGenerator;
|
||||
@@ -46,12 +53,6 @@ import com.github.victools.jsonschema.module.jackson.JacksonModule;
|
||||
import com.github.victools.jsonschema.module.jackson.JacksonOption;
|
||||
import com.github.victools.jsonschema.module.swagger2.Swagger2Module;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Utility class for manipulating {@link ModelOptions} objects.
|
||||
*
|
||||
@@ -61,10 +62,11 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public abstract class ModelOptionsUtils {
|
||||
|
||||
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
|
||||
public static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
|
||||
.registerModule(new JavaTimeModule());
|
||||
.addModules(JacksonUtils.instantiateAvailableModules())
|
||||
.build();
|
||||
|
||||
private static final List<String> BEAN_MERGE_FIELD_EXCISIONS = List.of("class");
|
||||
|
||||
|
||||
@@ -21,18 +21,22 @@ import java.util.function.Function;
|
||||
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
|
||||
* that can be consumed by the Model. The default implementation converts the output into
|
||||
* String before sending it to the Model. Provide a custom function responseConverter
|
||||
* implementation to override this.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Sebastien Deleuze
|
||||
*
|
||||
*/
|
||||
public class FunctionCallbackWrapper<I, O> extends AbstractFunctionCallback<I, O> {
|
||||
@@ -90,10 +94,7 @@ public class FunctionCallbackWrapper<I, O> extends AbstractFunctionCallback<I, O
|
||||
|
||||
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");
|
||||
@@ -142,7 +143,14 @@ public class FunctionCallbackWrapper<I, O> extends AbstractFunctionCallback<I, O
|
||||
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) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
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;
|
||||
@@ -30,6 +31,8 @@ import com.github.victools.jsonschema.module.jackson.JacksonModule;
|
||||
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;
|
||||
|
||||
@@ -91,7 +94,7 @@ public class BeanOutputParser<T> implements OutputParser<T> {
|
||||
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);
|
||||
@@ -142,9 +145,10 @@ public class BeanOutputParser<T> implements OutputParser<T> {
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2024 - 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.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,12 +32,14 @@ import java.util.Objects;
|
||||
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;
|
||||
@@ -64,11 +66,14 @@ import io.micrometer.observation.ObservationRegistry;
|
||||
* @author Dingmeng Xue
|
||||
* @author Mark Pollack
|
||||
* @author Christian Tzolov
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
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;
|
||||
@@ -84,6 +89,7 @@ public class SimpleVectorStore extends AbstractObservationVectorStore {
|
||||
|
||||
Objects.requireNonNull(embeddingModel, "EmbeddingModel must not be null");
|
||||
this.embeddingModel = embeddingModel;
|
||||
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,9 +178,8 @@ public class SimpleVectorStore extends AbstractObservationVectorStore {
|
||||
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) {
|
||||
@@ -189,9 +194,8 @@ public class SimpleVectorStore extends AbstractObservationVectorStore {
|
||||
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) {
|
||||
@@ -200,8 +204,7 @@ public class SimpleVectorStore extends AbstractObservationVectorStore {
|
||||
}
|
||||
|
||||
private String getVectorDbAsJson() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
|
||||
ObjectWriter objectWriter = this.objectMapper.writerWithDefaultPrettyPrinter();
|
||||
String json;
|
||||
try {
|
||||
json = objectWriter.writeValueAsString(this.store);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -50,9 +50,9 @@ public class BedrockCohereChatAutoConfiguration {
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public CohereChatBedrockApi cohereChatApi(AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, BedrockCohereChatProperties properties,
|
||||
BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
|
||||
return new CohereChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -51,9 +51,9 @@ public class BedrockCohereEmbeddingAutoConfiguration {
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public CohereEmbeddingBedrockApi cohereEmbeddingApi(AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, BedrockCohereEmbeddingProperties properties,
|
||||
BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
|
||||
return new CohereEmbeddingBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -52,9 +52,9 @@ public class BedrockAi21Jurassic2ChatAutoConfiguration {
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public Ai21Jurassic2ChatBedrockApi ai21Jurassic2ChatBedrockApi(AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, BedrockAi21Jurassic2ChatProperties properties,
|
||||
BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
|
||||
return new Ai21Jurassic2ChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -52,9 +52,10 @@ public class BedrockLlamaChatAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public LlamaChatBedrockApi llamaApi(AwsCredentialsProvider credentialsProvider, AwsRegionProvider regionProvider,
|
||||
BedrockLlamaChatProperties properties, BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockLlamaChatProperties properties, BedrockAwsConnectionProperties awsProperties,
|
||||
ObjectMapper objectMapper) {
|
||||
return new LlamaChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -50,9 +50,9 @@ public class BedrockTitanChatAutoConfiguration {
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public TitanChatBedrockApi titanChatBedrockApi(AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, BedrockTitanChatProperties properties,
|
||||
BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
|
||||
return new TitanChatBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -51,9 +51,9 @@ public class BedrockTitanEmbeddingAutoConfiguration {
|
||||
@ConditionalOnBean({ AwsCredentialsProvider.class, AwsRegionProvider.class })
|
||||
public TitanEmbeddingBedrockApi titanEmbeddingBedrockApi(AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, BedrockTitanEmbeddingProperties properties,
|
||||
BedrockAwsConnectionProperties awsProperties) {
|
||||
BedrockAwsConnectionProperties awsProperties, ObjectMapper objectMapper) {
|
||||
return new TitanEmbeddingBedrockApi(properties.getModel(), credentialsProvider, regionProvider.getRegion(),
|
||||
new ObjectMapper(), awsProperties.getTimeout());
|
||||
objectMapper, awsProperties.getTimeout());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -39,6 +39,7 @@ import io.micrometer.observation.ObservationRegistry;
|
||||
* @author Christian Tzolov
|
||||
* @author Eddú Meléndez
|
||||
* @author Soby Chacko
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ EmbeddingModel.class, RestClient.class, ChromaVectorStore.class, ObjectMapper.class })
|
||||
@@ -54,12 +55,13 @@ public class ChromaVectorStoreAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ChromaApi chromaApi(ChromaApiProperties apiProperties,
|
||||
ObjectProvider<RestClient.Builder> restClientBuilderProvider, ChromaConnectionDetails connectionDetails) {
|
||||
ObjectProvider<RestClient.Builder> restClientBuilderProvider, ChromaConnectionDetails connectionDetails,
|
||||
ObjectMapper objectMapper) {
|
||||
|
||||
String chromaUrl = String.format("%s:%s", connectionDetails.getHost(), connectionDetails.getPort());
|
||||
|
||||
var chromaApi = new ChromaApi(chromaUrl, restClientBuilderProvider.getIfAvailable(RestClient::builder),
|
||||
new ObjectMapper());
|
||||
objectMapper);
|
||||
|
||||
if (StringUtils.hasText(connectionDetails.getKeyToken())) {
|
||||
chromaApi.withKeyToken(connectionDetails.getKeyToken());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.testcontainers.service.connection.chroma;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -83,6 +84,11 @@ class ChromaContainerConnectionDetailsFactoryTest {
|
||||
@ImportAutoConfiguration(ChromaVectorStoreAutoConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new TransformersEmbeddingModel();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.testcontainers.service.connection.chroma;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -87,6 +88,11 @@ class ChromaWithToken2ContainerConnectionDetailsFactoryTest {
|
||||
@ImportAutoConfiguration(ChromaVectorStoreAutoConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new TransformersEmbeddingModel();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.testcontainers.service.connection.chroma;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -85,6 +86,11 @@ class ChromaWithTokenContainerConnectionDetailsFactoryTest {
|
||||
@ImportAutoConfiguration(ChromaVectorStoreAutoConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new TransformersEmbeddingModel();
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
|
||||
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
|
||||
import org.springframework.ai.observation.conventions.VectorStoreProvider;
|
||||
import org.springframework.ai.util.JacksonUtils;
|
||||
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
|
||||
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
|
||||
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
|
||||
@@ -42,6 +43,9 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
/**
|
||||
@@ -50,10 +54,11 @@ import io.micrometer.observation.ObservationRegistry;
|
||||
* their similarity to a query, using the {@link ChromaApi} and {@link EmbeddingModel} for
|
||||
* embedding calculations. For more information about how it does this, see the official
|
||||
* <a href="https://www.trychroma.com/">Chroma website</a>.
|
||||
*
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Fu Cheng
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*
|
||||
*/
|
||||
public class ChromaVectorStore extends AbstractObservationVectorStore implements InitializingBean {
|
||||
|
||||
@@ -79,6 +84,8 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
|
||||
|
||||
private final BatchingStrategy batchingStrategy;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, boolean initializeSchema) {
|
||||
this(embeddingModel, chromaApi, DEFAULT_COLLECTION_NAME, initializeSchema);
|
||||
}
|
||||
@@ -101,6 +108,7 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
|
||||
this.initializeSchema = initializeSchema;
|
||||
this.filterExpressionConverter = new ChromaFilterExpressionConverter();
|
||||
this.batchingStrategy = batchingStrategy;
|
||||
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
|
||||
}
|
||||
|
||||
public void setFilterExpressionConverter(FilterExpressionConverter filterExpressionConverter) {
|
||||
@@ -152,8 +160,8 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
|
||||
Assert.notNull(query, "Query string must not be null");
|
||||
|
||||
float[] embedding = this.embeddingModel.embed(query);
|
||||
Map<String, Object> where = (StringUtils.hasText(nativeFilterExpression))
|
||||
? JsonUtils.jsonToMap(nativeFilterExpression) : Map.of();
|
||||
Map<String, Object> where = (StringUtils.hasText(nativeFilterExpression)) ? jsonToMap(nativeFilterExpression)
|
||||
: Map.of();
|
||||
var queryRequest = new ChromaApi.QueryRequest(embedding, request.getTopK(), where);
|
||||
var queryResponse = this.chromaApi.queryCollection(this.collectionId, queryRequest);
|
||||
var embeddings = this.chromaApi.toEmbeddingResponseList(queryResponse);
|
||||
@@ -179,6 +187,16 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
|
||||
return responseDocuments;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> jsonToMap(String jsonText) {
|
||||
try {
|
||||
return (Map<String, Object>) this.objectMapper.readValue(jsonText, Map.class);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return this.collectionName;
|
||||
}
|
||||
|
||||
@@ -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.vectorstore;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Utility class for JSON processing. Provides methods for converting JSON strings to maps
|
||||
* and lists, and for converting between lists of different numeric types.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class JsonUtils {
|
||||
|
||||
/**
|
||||
* Converts a JSON string to a map.
|
||||
* @param jsonText the JSON string to convert
|
||||
* @return the map representation of the JSON string
|
||||
* @throws RuntimeException if an error occurs during the conversion
|
||||
*/
|
||||
public static Map<String, Object> jsonToMap(String jsonText) {
|
||||
try {
|
||||
return (Map<String, Object>) new ObjectMapper().readValue(jsonText, Map.class);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Converts a list of doubles to a list of floats.
|
||||
// * @param embeddingDouble the list of doubles to convert
|
||||
// * @return the list of floats
|
||||
// */
|
||||
// public static List<Float> toFloatList(List<Double> embeddingDouble) {
|
||||
// return embeddingDouble.stream().map(Number::floatValue).toList();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Converts a list of doubles to a float array.
|
||||
* @param embedding the list of doubles to convert
|
||||
* @return the float array
|
||||
*/
|
||||
public static float[] toFloatArray(List<Float> embedding) {
|
||||
float[] embeddingFloat = new float[embedding.size()];
|
||||
int i = 0;
|
||||
for (Float d : embedding) {
|
||||
embeddingFloat[i++] = d.floatValue();
|
||||
}
|
||||
return embeddingFloat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of floats to a list of doubles.
|
||||
* @param floats the list of floats to convert
|
||||
* @return the list of doubles
|
||||
*/
|
||||
public static List<Double> toDouble(List<Float> floats) {
|
||||
return floats.stream().map(f -> f.doubleValue()).toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.document.Document;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
|
||||
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
|
||||
import org.springframework.ai.observation.conventions.VectorStoreProvider;
|
||||
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;
|
||||
@@ -62,6 +64,7 @@ import reactor.util.annotation.NonNull;
|
||||
* @author Christian Tzolov
|
||||
* @author Thomas Vitale
|
||||
* @author Soby Chacko
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class GemFireVectorStore extends AbstractObservationVectorStore implements InitializingBean {
|
||||
|
||||
@@ -81,6 +84,8 @@ public class GemFireVectorStore extends AbstractObservationVectorStore implement
|
||||
|
||||
private final BatchingStrategy batchingStrategy;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Configures and initializes a GemFireVectorStore instance based on the provided
|
||||
* configuration.
|
||||
@@ -127,6 +132,7 @@ public class GemFireVectorStore extends AbstractObservationVectorStore implement
|
||||
.toString();
|
||||
this.client = WebClient.create(base);
|
||||
this.batchingStrategy = batchingStrategy;
|
||||
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
|
||||
}
|
||||
|
||||
// Create Index Parameters
|
||||
@@ -419,10 +425,9 @@ public class GemFireVectorStore extends AbstractObservationVectorStore implement
|
||||
document.getContent(), document.getMetadata()))
|
||||
.toList());
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String embeddingsJson = null;
|
||||
try {
|
||||
String embeddingString = objectMapper.writeValueAsString(upload);
|
||||
String embeddingString = this.objectMapper.writeValueAsString(upload);
|
||||
embeddingsJson = embeddingString.substring("{\"embeddings\":".length());
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
@@ -498,8 +503,7 @@ public class GemFireVectorStore extends AbstractObservationVectorStore implement
|
||||
createRequest.setVectorSimilarityFunction(vectorSimilarityFunction);
|
||||
createRequest.setFields(fields);
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String index = objectMapper.writeValueAsString(createRequest);
|
||||
String index = this.objectMapper.writeValueAsString(createRequest);
|
||||
|
||||
client.post()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.ai.vectorstore;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -26,6 +28,7 @@ import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.model.EmbeddingUtils;
|
||||
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.VectorStoreObservationContext.Builder;
|
||||
@@ -64,6 +67,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author Rahul Mittal
|
||||
* @author Christian Tzolov
|
||||
* @author Sebastien Deleuze
|
||||
* @see <a href=
|
||||
* "https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/introduction">SAP
|
||||
* HANA Database Vector Engine Guide</a>
|
||||
@@ -79,6 +83,8 @@ public class HanaCloudVectorStore extends AbstractObservationVectorStore {
|
||||
|
||||
private final HanaCloudVectorStoreConfig config;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public HanaCloudVectorStore(HanaVectorRepository<? extends HanaVectorEntity> repository,
|
||||
EmbeddingModel embeddingModel, HanaCloudVectorStoreConfig config) {
|
||||
|
||||
@@ -94,6 +100,7 @@ public class HanaCloudVectorStore extends AbstractObservationVectorStore {
|
||||
this.repository = repository;
|
||||
this.embeddingModel = embeddingModel;
|
||||
this.config = config;
|
||||
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +149,7 @@ public class HanaCloudVectorStore extends AbstractObservationVectorStore {
|
||||
|
||||
return searchResult.stream().map(c -> {
|
||||
try {
|
||||
return new Document(c.get_id(), c.toJson(), Collections.emptyMap());
|
||||
return new Document(c.get_id(), this.objectMapper.writeValueAsString(c), Collections.emptyMap());
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.ai.vectorstore;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
@@ -40,10 +38,6 @@ public abstract class HanaVectorEntity {
|
||||
public HanaVectorEntity() {
|
||||
}
|
||||
|
||||
public String toJson() throws JsonProcessingException {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
}
|
||||
|
||||
public String get_id() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.ai.vectorstore;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.pgvector.PGvector;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.postgresql.util.PGobject;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
|
||||
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
|
||||
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.filter.FilterExpressionConverter;
|
||||
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
|
||||
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
|
||||
@@ -60,6 +62,7 @@ import java.util.UUID;
|
||||
* @author Muthukumaran Navaneethakrishnan
|
||||
* @author Thomas Vitale
|
||||
* @author Soby Chacko
|
||||
* @author Sebastien Deleuze
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PgVectorStore extends AbstractObservationVectorStore implements InitializingBean {
|
||||
@@ -100,7 +103,7 @@ public class PgVectorStore extends AbstractObservationVectorStore implements Ini
|
||||
|
||||
private final PgDistanceType distanceType;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final boolean removeExistingVectorStoreTable;
|
||||
|
||||
@@ -154,6 +157,8 @@ public class PgVectorStore extends AbstractObservationVectorStore implements Ini
|
||||
|
||||
super(observationRegistry, customObservationConvention);
|
||||
|
||||
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
|
||||
|
||||
this.vectorTableName = (null == vectorTableName || vectorTableName.isEmpty()) ? DEFAULT_TABLE_NAME
|
||||
: vectorTableName.trim();
|
||||
logger.info("Using the vector table name: {}. Is empty: {}", this.vectorTableName,
|
||||
@@ -231,7 +236,7 @@ public class PgVectorStore extends AbstractObservationVectorStore implements Ini
|
||||
|
||||
private String toJson(Map<String, Object> map) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(map);
|
||||
return this.objectMapper.writeValueAsString(map);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
Reference in New Issue
Block a user