add beanoutput parser

This commit is contained in:
Mark Pollack
2023-08-21 18:15:37 -07:00
parent e3da80d86e
commit 99f3c1da19
6 changed files with 153 additions and 0 deletions

View File

@@ -67,6 +67,7 @@
<open-ai-client.version>0.12.0</open-ai-client.version>
<azure-open-ai-client.version>1.0.0-beta.3</azure-open-ai-client.version>
<jtokkit.version>0.6.1</jtokkit.version>
<victools.version>4.31.1</victools.version>
<!-- documentation dependencies -->
<io.spring.maven.antora-version>0.0.4</io.spring.maven.antora-version>

View File

@@ -43,6 +43,24 @@
<version>${jtokkit.version}</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
<version>4.31.1</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-module-jackson</artifactId>
<version>4.31.1</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.kjetland</groupId>-->
<!-- <artifactId>mbknor-jackson-jsonschema_2.13</artifactId>-->
<!-- <version>1.0.39</version>-->
<!-- </dependency>-->
<!-- test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -0,0 +1,68 @@
package org.springframework.ai.parser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.victools.jsonschema.generator.OptionPreset;
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.generator.SchemaVersion;
import org.springframework.ai.prompt.PromptTemplate;
import java.util.Map;
import java.util.Objects;
public class BeanOutputParser<T> implements OutputParser<T> {
private String jsonSchema;
private Class clazz;
public BeanOutputParser(Class clazz) {
Objects.requireNonNull(clazz, "Java Class can not be null;");
this.clazz = clazz;
generateSchema();
}
private void generateSchema() {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12,
OptionPreset.PLAIN_JSON);
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonSchema = generator.generateSchema(this.clazz);
this.jsonSchema = jsonSchema.toPrettyString();
}
@Override
public T parse(String text) {
ObjectMapper objectMapper = new ObjectMapper();
try {
return (T) objectMapper.readValue(text, this.clazz);
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
public String getFormat() {
String raw = """
Your response should be in JSON format.
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
Here is the JSON Schema instance your output must adhere to in the enclosed markdown codeblock:
```json
{jsonSchema}
```
""";
PromptTemplate promptTemplate = new PromptTemplate(raw);
return promptTemplate.render(Map.of("jsonSchema", this.jsonSchema));
}
// {"type":"object","properties":{"answer":{"type":"string","description":"answer to
// the user's question"},"source":{"type":"string","description":"source used to
// answer the user's question, should be a
// website."}},"required":["answer","source"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
//
}

View File

@@ -33,6 +33,18 @@
<version>${open-ai-client.version}</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
<version>${victools.version}</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-module-jackson</artifactId>
<version>${victools.version}</version>
</dependency>
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>

View File

@@ -0,0 +1,35 @@
package org.springframework.ai.openai.client;
import java.util.List;
public class ActorsFilms {
private String actor;
private List<String> movies;
public ActorsFilms() {
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public List<String> getMovies() {
return movies;
}
public void setMovies(List<String> movies) {
this.movies = movies;
}
@Override
public String toString() {
return "ActorsFilms{" + "actor='" + actor + '\'' + ", movies=" + movies + '}';
}
}

View File

@@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.client.Generation;
import org.springframework.ai.openai.testutils.AbstractIntegrationTest;
import org.springframework.ai.parser.BeanOutputParser;
import org.springframework.ai.parser.ListOutputParser;
import org.springframework.ai.parser.MapOutputParser;
import org.springframework.ai.prompt.Prompt;
@@ -81,4 +82,22 @@ class ClientIntegrationTests extends AbstractIntegrationTest {
}
@Test
void beanOutputParser() {
BeanOutputParser<ActorsFilms> outputParser = new BeanOutputParser<>(ActorsFilms.class);
String format = outputParser.getFormat();
String template = """
Generate the filmography for a random actor.
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
Prompt prompt = new Prompt(promptTemplate.createMessage());
Generation generation = openAiClient.generate(prompt).getGeneration();
ActorsFilms actorsFilms = outputParser.parse(generation.getText());
System.out.println(actorsFilms);
}
}