Skip to content

Commit

Permalink
Add Ollama structured output JSON example
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasVitale committed Dec 19, 2024
1 parent 2e71458 commit 2015067
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
6 changes: 6 additions & 0 deletions patterns/structured-output/structured-output-ollama/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ http POST :8080/chat/map -b
```shell
http :8080/chat/list genre="rock" instrument="piano" -b
```

Ollama has also a native structured output feature, used in the following request.

```shell
http :8080/chat/json country=="Denmark" -b
```
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.thomasvitale.ai.spring;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -37,7 +39,7 @@ ArtistInfo chatBeanOutput(@RequestBody MusicQuestion question) {
.param("instrument", question.instrument())
)
.options(OllamaOptions.builder()
.withFormat("json")
.format("json")
.build())
.call()
.entity(ArtistInfo.class);
Expand Down Expand Up @@ -69,4 +71,23 @@ List<String> chatListOutput(@RequestBody MusicQuestion question) {
.entity(new ListOutputConverter(new DefaultConversionService()));
}

@GetMapping("/chat/json")
CountryInfo chatJsonOutput(String country) {
var outputConverter = new BeanOutputConverter<>(CountryInfo.class);
var userPromptTemplate = """
Tell me about {country}.
""";

return chatClient.prompt()
.user(userSpec -> userSpec
.text(userPromptTemplate)
.param("country", country)
)
.options(OllamaOptions.builder()
.format(outputConverter.getJsonSchemaMap())
.build())
.call()
.entity(outputConverter);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.thomasvitale.ai.spring;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

public record CountryInfo(
@JsonProperty(required = true) String name,
@JsonProperty(required = true) String capital,
@JsonProperty(required = true) List<String> languages
) {}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.thomasvitale.ai.spring.model;

import com.thomasvitale.ai.spring.ArtistInfo;
import com.thomasvitale.ai.spring.CountryInfo;
import com.thomasvitale.ai.spring.MusicQuestion;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.ai.ollama.api.OllamaModel;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -42,7 +45,7 @@ ArtistInfo chatBeanOutput(@RequestBody MusicQuestion question) {
"genre", question.genre(),
"format", outputConverter.getFormat());
var prompt = userPromptTemplate.create(model, OllamaOptions.builder()
.withFormat("json")
.format("json")
.build());

var chatResponse = chatModel.call(prompt);
Expand Down Expand Up @@ -77,6 +80,22 @@ List<String> chatListOutput(@RequestBody MusicQuestion question) {
"format", outputConverter.getFormat());
var prompt = userPromptTemplate.create(model);

var chatResponse = chatModel.call(prompt);
return outputConverter.convert(chatResponse.getResult().getOutput().getContent());
}

@GetMapping("/chat/json")
CountryInfo chatJsonOutput(String country) {
var outputConverter = new BeanOutputConverter<>(CountryInfo.class);
var userPromptTemplate = new PromptTemplate("""
Tell me about {country}.
""");
Map<String,Object> model = Map.of("country", country);
var prompt = userPromptTemplate.create(model, OllamaOptions.builder()
.model(OllamaModel.LLAMA3_2.getName())
.format(outputConverter.getJsonSchemaMap())
.build());

var chatResponse = chatModel.call(prompt);
return outputConverter.convert(chatResponse.getResult().getOutput().getText());
}
Expand Down

0 comments on commit 2015067

Please sign in to comment.