Fix Ollama client options NPE and lack for SYS message handling

- Fix the Ollama options merging to pervent NPE.
 - Fix the Ollama handling for SYS messages.
 - Fix the BeanOutputParser to support JSON Schema reponses.
 - All Ollama Parsers tests pass now.

 Resolves: #258 ,  #273
This commit is contained in:
Christian Tzolov
2024-01-26 15:29:05 +01:00
parent aa8c3856ed
commit 944c4ebbaa
5 changed files with 27 additions and 15 deletions

View File

@@ -132,13 +132,13 @@ public class OllamaChatClient implements ChatClient, StreamingChatClient {
List<OllamaApi.Message> ollamaMessages = prompt.getInstructions()
.stream()
.filter(message -> message.getMessageType() == MessageType.USER
|| message.getMessageType() == MessageType.ASSISTANT)
|| message.getMessageType() == MessageType.ASSISTANT
|| message.getMessageType() == MessageType.SYSTEM)
.map(m -> OllamaApi.Message.builder(toRole(m)).withContent(m.getContent()).build())
.toList();
// runtime options
Map<String, Object> promptOptions = objectToMap(prompt.getOptions());
Map<String, Object> clientOptionsToUse = merge(promptOptions, this.clientOptions, HashMap.class);
Map<String, Object> clientOptionsToUse = merge(prompt.getOptions(), this.clientOptions, HashMap.class);
return ChatRequest.builder(model)
.withStream(stream)
@@ -169,6 +169,9 @@ public class OllamaChatClient implements ChatClient, StreamingChatClient {
}
public static <T> T merge(Object source, Object target, Class<T> clazz) {
if (source == null) {
source = Map.of();
}
Map<String, Object> sourceMap = objectToMap(source);
Map<String, Object> targetMap = objectToMap(target);

View File

@@ -47,7 +47,7 @@ class OllamaChatClientIT {
private static final Log logger = LogFactory.getLog(OllamaChatClientIT.class);
@Container
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.16").withExposedPorts(11434);
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.21").withExposedPorts(11434);
static String baseUrl;
@@ -86,7 +86,6 @@ class OllamaChatClientIT {
assertThat(response.getResult().getOutput().getContent()).contains("Blackbeard");
}
@Disabled("TODO: Fix the parser instructions to return the correct format")
@Test
void outputParser() {
DefaultConversionService conversionService = new DefaultConversionService();
@@ -106,7 +105,6 @@ class OllamaChatClientIT {
assertThat(list).hasSize(5);
}
@Disabled("TODO: Fix the parser instructions to return the correct format")
@Test
void mapOutputParser() {
MapOutputParser outputParser = new MapOutputParser();
@@ -131,7 +129,6 @@ class OllamaChatClientIT {
record ActorsFilmsRecord(String actor, List<String> movies) {
}
@Disabled("TODO: Fix the parser instructions to return the correct format")
@Test
void beanOutputParserRecords() {
@@ -141,7 +138,6 @@ class OllamaChatClientIT {
String template = """
Generate the filmography of 5 movies for Tom Hanks.
{format}
Remove Markdown code blocks from the output.
""";
PromptTemplate promptTemplate = new PromptTemplate(template, Map.of("format", format));
Prompt prompt = new Prompt(promptTemplate.createMessage());
@@ -152,7 +148,6 @@ class OllamaChatClientIT {
assertThat(actorsFilms.movies()).hasSize(5);
}
@Disabled("TODO: Fix the parser instructions to return the correct format")
@Test
void beanStreamOutputParserRecords() {

View File

@@ -30,7 +30,7 @@ class OllamaEmbeddingClientIT {
private static final Log logger = LogFactory.getLog(OllamaApiIT.class);
@Container
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.16").withExposedPorts(11434);
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.21").withExposedPorts(11434);
static String baseUrl;

View File

@@ -51,7 +51,7 @@ public class OllamaApiIT {
private static final Log logger = LogFactory.getLog(OllamaApiIT.class);
@Container
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.16").withExposedPorts(11434);
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama:0.1.21").withExposedPorts(11434);
static OllamaApi ollamaApi;
@@ -87,9 +87,14 @@ public class OllamaApiIT {
var request = ChatRequest.builder("orca-mini")
.withStream(false)
.withMessages(List.of(Message.builder(Role.USER)
.withContent("What is the capital of Bulgaria and what is the size? " + "What it the national anthem?")
.build()))
.withMessages(List.of(
Message.builder(Role.SYSTEM)
.withContent("You are geography teacher. You are talking to a student.")
.build(),
Message.builder(Role.USER)
.withContent("What is the capital of Bulgaria and what is the size? "
+ "What it the national anthem?")
.build()))
.withOptions(OllamaOptions.create().withTemperature(0.9f))
.build();
@@ -127,7 +132,7 @@ public class OllamaApiIT {
.collect(Collectors.joining("\n"))).contains("Sofia");
ChatResponse lastResponse = responses.get(responses.size() - 1);
assertThat(lastResponse.message()).isNull();
assertThat(lastResponse.message().content()).isEmpty();
assertThat(lastResponse.done()).isTrue();
}

View File

@@ -25,6 +25,7 @@ import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.module.jackson.JacksonModule;
import java.util.Map;
import java.util.Objects;
import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON;
@@ -95,6 +96,13 @@ public class BeanOutputParser<T> implements OutputParser<T> {
*/
public T parse(String text) {
try {
// If the response is a JSON Schema, extract the properties and use them as
// the
// response.
Map<String, Object> map = this.objectMapper.readValue(text, Map.class);
if (map.containsKey("$schema")) {
text = this.objectMapper.writeValueAsString(map.get("properties"));
}
return (T) this.objectMapper.readValue(text, this.clazz);
}
catch (JsonProcessingException e) {
@@ -122,6 +130,7 @@ public class BeanOutputParser<T> implements OutputParser<T> {
String template = """
Your response should be in JSON format.
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
Do not include markdown code blocks in your response.
Here is the JSON Schema instance your output must adhere to:
```%s```
""";