diff --git a/spring-ai-core/src/main/java/org/springframework/ai/parser/AbstractMessageConverterOutputParser.java b/spring-ai-core/src/main/java/org/springframework/ai/parser/AbstractMessageConverterOutputParser.java new file mode 100644 index 000000000..2514e9c6c --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/parser/AbstractMessageConverterOutputParser.java @@ -0,0 +1,17 @@ +package org.springframework.ai.parser; + +import org.springframework.messaging.converter.MessageConverter; + +public abstract class AbstractMessageConverterOutputParser implements OutputParser { + + private MessageConverter messageConverter; + + public AbstractMessageConverterOutputParser(MessageConverter messageConverter) { + this.messageConverter = messageConverter; + } + + public MessageConverter getMessageConverter() { + return messageConverter; + } + +} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/parser/JsonOutputParser.java b/spring-ai-core/src/main/java/org/springframework/ai/parser/JsonOutputParser.java new file mode 100644 index 000000000..45b80b243 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/parser/JsonOutputParser.java @@ -0,0 +1,38 @@ +package org.springframework.ai.parser; + +import org.springframework.messaging.Message; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.support.MessageBuilder; + +import java.nio.charset.StandardCharsets; + +/** + * Uses Jackson + */ +public class JsonOutputParser extends AbstractMessageConverterOutputParser { + + private Class dataType; + + public JsonOutputParser(Class dataType) { + super(new MappingJackson2MessageConverter()); + this.dataType = dataType; + } + + @Override + public Object parse(String text) { + Message message = MessageBuilder.withPayload(text.getBytes(StandardCharsets.UTF_8)).build(); + return getMessageConverter().fromMessage(message, dataType); + } + + @Override + public String getFormat() { + String raw = """ + Your response should be in JSON format. + The data structure for the JSON should match this Java class: %s + Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. + """; + return String.format(raw, dataType.getCanonicalName()); + } + +} diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/ClientIntegrationTests.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/ClientIntegrationTests.java index d03974a4e..bbab2ad83 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/ClientIntegrationTests.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/client/ClientIntegrationTests.java @@ -1,9 +1,8 @@ package org.springframework.ai.openai.client; import org.junit.jupiter.api.Test; -import org.springframework.ai.client.AiClient; import org.springframework.ai.client.Generation; -import org.springframework.ai.parser.OutputParser; +import org.springframework.ai.parser.JsonOutputParser; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.PromptTemplate; @@ -17,6 +16,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.io.Resource; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -85,4 +86,25 @@ class ClientIntegrationTests { } + @Test + void jsonOutputParser() { + JsonOutputParser outputParser = new JsonOutputParser(HashMap.class); + + String format = outputParser.getFormat(); + String template = """ + Provide me a List of {subject} + {format} + """; + PromptTemplate promptTemplate = new PromptTemplate(template, + Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format)); + Prompt prompt = new Prompt(promptTemplate.createMessage()); + Generation generation = openAiClient.generate(prompt).getGeneration(); + + Object result = outputParser.parse(generation.getText()); + System.out.println(result); + assertThat(result).isNotNull(); + assertThat(((Map) result).get("numbers")).isEqualTo(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)); + + } + }