Add payment transaction status Function Calling ITs - OpenAI and Vertex AI Gemeini
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.openai.chat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.client.AdvisedRequest;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.client.RequestResponseAdvisor;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.openai.api.OpenAiApi.ChatModel;
|
||||
import org.springframework.ai.retry.RetryUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Description;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_PROJECT_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_LOCATION", matches = ".*")
|
||||
public class OpenAiPaymentTransactionIT {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(OpenAiPaymentTransactionIT.class);
|
||||
|
||||
@Autowired
|
||||
ChatClient chatClient;
|
||||
|
||||
record TransactionStatusResponse(String id, String status) {
|
||||
}
|
||||
|
||||
private static class LoggingAdvisor implements RequestResponseAdvisor {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LoggingAdvisor.class);
|
||||
|
||||
@Override
|
||||
public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
|
||||
logger.info("System text: \n" + request.systemText());
|
||||
logger.info("System params: " + request.systemParams());
|
||||
logger.info("User text: \n" + request.userText());
|
||||
logger.info("User params:" + request.userParams());
|
||||
logger.info("Function names: " + request.functionNames());
|
||||
|
||||
logger.info("Options: " + request.chatOptions().toString());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
|
||||
logger.info("Response: " + response);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||
@ValueSource(strings = { "paymentStatus", "paymentStatuses" })
|
||||
public void transactionPaymentStatuses(String functionName) {
|
||||
List<TransactionStatusResponse> content = this.chatClient.prompt()
|
||||
.advisors(new LoggingAdvisor())
|
||||
.functions(functionName)
|
||||
.user("""
|
||||
What is the status of my payment transactions 001, 002 and 003?
|
||||
""")
|
||||
.call()
|
||||
.entity(new ParameterizedTypeReference<List<TransactionStatusResponse>>() {
|
||||
});
|
||||
|
||||
logger.info("" + content);
|
||||
|
||||
assertThat(content.get(0).id()).isEqualTo("001");
|
||||
assertThat(content.get(0).status()).isEqualTo("pending");
|
||||
|
||||
assertThat(content.get(1).id()).isEqualTo("002");
|
||||
assertThat(content.get(1).status()).isEqualTo("approved");
|
||||
|
||||
assertThat(content.get(2).id()).isEqualTo("003");
|
||||
assertThat(content.get(2).status()).isEqualTo("rejected");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||
@ValueSource(strings = { "paymentStatus", "paymentStatuses" })
|
||||
public void streamingPaymentStatuses(String functionName) {
|
||||
|
||||
var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<TransactionStatusResponse>>() {
|
||||
});
|
||||
|
||||
Flux<String> flux = this.chatClient.prompt()
|
||||
.advisors(new LoggingAdvisor())
|
||||
.functions(functionName)
|
||||
.user(u -> u.text("""
|
||||
What is the status of my payment transactions 001, 002 and 003?
|
||||
|
||||
{format}
|
||||
""").param("format", converter.getFormat()))
|
||||
.stream()
|
||||
.content();
|
||||
|
||||
String content = flux.collectList().block().stream().collect(Collectors.joining());
|
||||
|
||||
List<TransactionStatusResponse> structure = converter.convert(content);
|
||||
logger.info("" + content);
|
||||
|
||||
assertThat(structure.get(0).id()).isEqualTo("001");
|
||||
assertThat(structure.get(0).status()).isEqualTo("pending");
|
||||
|
||||
assertThat(structure.get(1).id()).isEqualTo("002");
|
||||
assertThat(structure.get(1).status()).isEqualTo("approved");
|
||||
|
||||
assertThat(structure.get(2).id()).isEqualTo("003");
|
||||
assertThat(structure.get(2).status()).isEqualTo("rejected");
|
||||
}
|
||||
|
||||
record Transaction(String id) {
|
||||
}
|
||||
|
||||
record Status(String name) {
|
||||
}
|
||||
|
||||
record Transactions(List<Transaction> transactions) {
|
||||
}
|
||||
|
||||
record Statuses(List<Status> statuses) {
|
||||
}
|
||||
|
||||
private static final Map<Transaction, Status> DATASET = Map.of(new Transaction("001"), new Status("pending"),
|
||||
new Transaction("002"), new Status("approved"), new Transaction("003"), new Status("rejected"));
|
||||
|
||||
@SpringBootConfiguration
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@Description("Get the status of a single payment transaction")
|
||||
public Function<Transaction, Status> paymentStatus() {
|
||||
return transaction -> {
|
||||
logger.info("Single transaction: " + transaction);
|
||||
return DATASET.get(transaction);
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Description("Get the list statuses of a list of payment transactions")
|
||||
public Function<Transactions, Statuses> paymentStatuses() {
|
||||
return transactions -> {
|
||||
logger.info("List of transactions: " + transactions);
|
||||
return new Statuses(transactions.transactions().stream().map(t -> DATASET.get(t)).toList());
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatClient chatClient(OpenAiChatModel chatModel) {
|
||||
return ChatClient.builder(chatModel).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiApi chatCompletionApi() {
|
||||
return new OpenAiApi(System.getenv("OPENAI_API_KEY"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiChatModel openAiClient(OpenAiApi openAiApi, FunctionCallbackContext functionCallbackContext) {
|
||||
return new OpenAiChatModel(openAiApi,
|
||||
OpenAiChatOptions.builder()
|
||||
.withModel(ChatModel.GPT_4_0_TURBO.getModelName())
|
||||
.withTemperature(0.1f)
|
||||
.build(),
|
||||
functionCallbackContext, RetryUtils.DEFAULT_RETRY_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because of the OPEN_API_SCHEMA type, the FunctionCallbackContext instance must
|
||||
* different from the other JSON schema types.
|
||||
*/
|
||||
@Bean
|
||||
public FunctionCallbackContext springAiFunctionManager(ApplicationContext context) {
|
||||
FunctionCallbackContext manager = new FunctionCallbackContext();
|
||||
manager.setApplicationContext(context);
|
||||
return manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -336,6 +336,19 @@ public class VertexAiGeminiChatOptions implements FunctionCallingOptions, ChatOp
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VertexAiGeminiChatOptions [stopSequences=" + stopSequences + ", temperature=" + temperature + ", topP="
|
||||
+ topP + ", topK=" + topK + ", candidateCount=" + candidateCount + ", maxOutputTokens="
|
||||
+ maxOutputTokens + ", model=" + model + ", functionCallbacks=" + functionCallbacks + ", functions="
|
||||
+ functions + ", getClass()=" + getClass() + ", getStopSequences()=" + getStopSequences()
|
||||
+ ", getTemperature()=" + getTemperature() + ", getTopP()=" + getTopP() + ", getTopK()=" + getTopK()
|
||||
+ ", getCandidateCount()=" + getCandidateCount() + ", getMaxOutputTokens()=" + getMaxOutputTokens()
|
||||
+ ", getModel()=" + getModel() + ", getFunctionCallbacks()=" + getFunctionCallbacks()
|
||||
+ ", getFunctions()=" + getFunctions() + ", hashCode()=" + hashCode() + ", toString()="
|
||||
+ super.toString() + "]";
|
||||
}
|
||||
|
||||
public static VertexAiGeminiChatOptions fromOptions(VertexAiGeminiChatOptions fromOptions) {
|
||||
VertexAiGeminiChatOptions options = new VertexAiGeminiChatOptions();
|
||||
options.setStopSequences(fromOptions.getStopSequences());
|
||||
|
||||
@@ -55,7 +55,7 @@ public class VertexAiGeminiChatModelFunctionCallingIT {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private VertexAiGeminiChatModel vertexGeminiClient;
|
||||
private VertexAiGeminiChatModel chatModel;
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() {
|
||||
@@ -107,7 +107,7 @@ public class VertexAiGeminiChatModelFunctionCallingIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = vertexGeminiClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
@@ -142,13 +142,13 @@ public class VertexAiGeminiChatModelFunctionCallingIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
ChatResponse response = vertexGeminiClient.call(new Prompt(messages, promptOptions));
|
||||
ChatResponse response = chatModel.call(new Prompt(messages, promptOptions));
|
||||
|
||||
logger.info("Response: {}", response);
|
||||
|
||||
assertThat(response.getResult().getOutput().getContent()).containsAnyOf("15.0", "15");
|
||||
|
||||
ChatResponse response2 = vertexGeminiClient
|
||||
ChatResponse response2 = chatModel
|
||||
.call(new Prompt("What is the payment status for transaction 696?", promptOptions));
|
||||
|
||||
logger.info("Response: {}", response2);
|
||||
@@ -176,7 +176,7 @@ public class VertexAiGeminiChatModelFunctionCallingIT {
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
Flux<ChatResponse> response = vertexGeminiClient.stream(new Prompt(messages, promptOptions));
|
||||
Flux<ChatResponse> response = chatModel.stream(new Prompt(messages, promptOptions));
|
||||
|
||||
String responseString = response.collectList()
|
||||
.block()
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.vertexai.gemini.function;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.cloud.vertexai.Transport;
|
||||
import com.google.cloud.vertexai.VertexAI;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ai.chat.client.AdvisedRequest;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.client.RequestResponseAdvisor;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.ai.model.function.FunctionCallbackWrapper.Builder.SchemaType;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatModel;
|
||||
import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatOptions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Description;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@Disabled("Vertex AI Gemini function calling is very unstable.")
|
||||
@SpringBootTest
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_PROJECT_ID", matches = ".*")
|
||||
@EnabledIfEnvironmentVariable(named = "VERTEX_AI_GEMINI_LOCATION", matches = ".*")
|
||||
public class VertexAiGeminiPaymentTransactionIT {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(VertexAiGeminiPaymentTransactionIT.class);
|
||||
|
||||
@Autowired
|
||||
ChatClient chatClient;
|
||||
|
||||
record TransactionStatusResponse(String id, String status) {
|
||||
}
|
||||
|
||||
private static class LoggingAdvisor implements RequestResponseAdvisor {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LoggingAdvisor.class);
|
||||
|
||||
@Override
|
||||
public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
|
||||
logger.info("System text: \n" + request.systemText());
|
||||
logger.info("System params: " + request.systemParams());
|
||||
logger.info("User text: \n" + request.userText());
|
||||
logger.info("User params:" + request.userParams());
|
||||
logger.info("Function names: " + request.functionNames());
|
||||
|
||||
logger.info("Options: " + request.chatOptions().toString());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
|
||||
logger.info("Response: " + response);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paymentStatuses() {
|
||||
String content = this.chatClient.prompt().advisors(new LoggingAdvisor()).functions("paymentStatus").user("""
|
||||
What is the status of my payment transactions 001, 002 and 003?
|
||||
|
||||
To answer this question invoke the 'paymentStatus' function per transaction.
|
||||
""").call().content();
|
||||
|
||||
logger.info("" + content);
|
||||
}
|
||||
|
||||
@RepeatedTest(10)
|
||||
public void streamingPaymentStatuses() {
|
||||
|
||||
Flux<String> streamContent = this.chatClient.prompt()
|
||||
.advisors(new LoggingAdvisor())
|
||||
.functions("paymentStatus")
|
||||
// .functions("paymentStatuses")
|
||||
.user("""
|
||||
What is the status of my payment transactions 001, 002 and 003?
|
||||
To answer this question invoke the paymentStatus function per transaction.
|
||||
Return the transaction id and the transaction status for each transaction.
|
||||
""")
|
||||
.stream()
|
||||
.content();
|
||||
|
||||
String content = streamContent.collectList().block().stream().collect(Collectors.joining());
|
||||
|
||||
logger.info(content);
|
||||
|
||||
// Quota rate
|
||||
try {
|
||||
Thread.sleep(20000);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
record Transaction(String id) {
|
||||
}
|
||||
|
||||
record Status(String name) {
|
||||
}
|
||||
|
||||
record Transactions(List<Transaction> transactions) {
|
||||
}
|
||||
|
||||
record Statuses(List<Status> statuses) {
|
||||
}
|
||||
|
||||
private static final Map<Transaction, Status> DATASET = Map.of(new Transaction("001"), new Status("pending"),
|
||||
new Transaction("002"), new Status("approved"), new Transaction("003"), new Status("rejected"));
|
||||
|
||||
@SpringBootConfiguration
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@Description("Get the status of a single payment transaction")
|
||||
public Function<Transaction, Status> paymentStatus() {
|
||||
return transaction -> {
|
||||
logger.info("Single Transaction: " + transaction);
|
||||
return DATASET.get(transaction);
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Description("Get the list statuses of a list of payment transactions")
|
||||
public Function<Transactions, Statuses> paymentStatuses() {
|
||||
return transactions -> {
|
||||
logger.info("Transactions: " + transactions);
|
||||
return new Statuses(transactions.transactions().stream().map(t -> DATASET.get(t)).toList());
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChatClient chatClient(VertexAiGeminiChatModel chatModel) {
|
||||
return ChatClient.builder(chatModel).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAI vertexAiApi() {
|
||||
|
||||
String projectId = System.getenv("VERTEX_AI_GEMINI_PROJECT_ID");
|
||||
String location = System.getenv("VERTEX_AI_GEMINI_LOCATION");
|
||||
|
||||
return new VertexAI.Builder().setLocation(location)
|
||||
.setProjectId(projectId)
|
||||
.setTransport(Transport.REST)
|
||||
// .setTransport(Transport.GRPC)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VertexAiGeminiChatModel vertexAiChatModel(VertexAI vertexAi, ApplicationContext context) {
|
||||
|
||||
FunctionCallbackContext functionCallbackContext = springAiFunctionManager(context);
|
||||
|
||||
return new VertexAiGeminiChatModel(vertexAi,
|
||||
VertexAiGeminiChatOptions.builder()
|
||||
.withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_PRO_1_5_FLASH)
|
||||
// .withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_PRO_1_5_PRO)
|
||||
.withTemperature(0.1f)
|
||||
// .withResponseMimeType(ResponseMimeType.JSON)
|
||||
.build(),
|
||||
functionCallbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because of the OPEN_API_SCHEMA type, the FunctionCallbackContext instance must
|
||||
* different from the other JSON schema types.
|
||||
*/
|
||||
private FunctionCallbackContext springAiFunctionManager(ApplicationContext context) {
|
||||
FunctionCallbackContext manager = new FunctionCallbackContext();
|
||||
manager.setSchemaType(SchemaType.OPEN_API_SCHEMA);
|
||||
manager.setApplicationContext(context);
|
||||
return manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,8 @@ import com.github.victools.jsonschema.generator.SchemaGenerator;
|
||||
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
|
||||
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
|
||||
import com.github.victools.jsonschema.module.jackson.JacksonModule;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.lang.NonNull;
|
||||
@@ -52,6 +54,8 @@ import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_
|
||||
*/
|
||||
public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BeanOutputConverter.class);
|
||||
|
||||
/** Holds the generated JSON schema for the target type. */
|
||||
private String jsonSchema;
|
||||
|
||||
@@ -147,6 +151,7 @@ public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
logger.error("Could not pretty print json schema for jsonNode: " + jsonNode);
|
||||
throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e);
|
||||
}
|
||||
}
|
||||
@@ -159,9 +164,13 @@ public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
|
||||
*/
|
||||
public T convert(@NonNull String text) {
|
||||
try {
|
||||
if (text.startsWith("```json") && text.endsWith("```")) {
|
||||
text = text.substring(7, text.length() - 3);
|
||||
}
|
||||
return (T) this.objectMapper.readValue(text, this.typeRef);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
logger.error("Could not parse the given text to the desired target type:" + text + " into " + this.typeRef);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user