CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-ai--spring-ai-model

Core model interfaces and abstractions for Spring AI framework providing portable API for chat, embeddings, images, audio, and tool calling across multiple AI providers

Overview
Eval results
Files

converters.mddocs/reference/

Structured Output Conversion

Framework for converting LLM text outputs into structured Java objects using JSON schema, supporting POJOs, lists, maps, and custom converters with response text cleaning utilities.

Capabilities

StructuredOutputConverter Interface

Main interface for converting text to structured objects.

public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {
    /**
     * Convert text to the target type.
     *
     * @param text the text to convert
     * @return the converted object
     */
    T convert(String text);

    /**
     * Get format instructions for the LLM.
     * These instructions tell the LLM how to format its output.
     *
     * @return the format instructions
     */
    String getFormat();
}

FormatProvider Interface

Provides format instructions for LLMs.

public interface FormatProvider {
    /**
     * Get format instructions for output formatting.
     *
     * @return the format instructions
     */
    String getFormat();
}

BeanOutputConverter

Converts text to POJOs using JSON schema.

public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
    /**
     * Construct a BeanOutputConverter for a class.
     *
     * @param clazz the target class
     */
    public BeanOutputConverter(Class<T> clazz);

    /**
     * Construct a BeanOutputConverter with custom ObjectMapper.
     *
     * @param clazz the target class
     * @param objectMapper the Jackson ObjectMapper
     */
    public BeanOutputConverter(Class<T> clazz, ObjectMapper objectMapper);

    /**
     * Construct a BeanOutputConverter for a parameterized type.
     *
     * @param typeRef the parameterized type reference
     */
    public BeanOutputConverter(ParameterizedTypeReference<T> typeRef);

    /**
     * Construct a BeanOutputConverter with type reference and ObjectMapper.
     *
     * @param typeRef the parameterized type reference
     * @param objectMapper the Jackson ObjectMapper
     */
    public BeanOutputConverter(ParameterizedTypeReference<T> typeRef, ObjectMapper objectMapper);

    /**
     * Convert text to bean.
     *
     * @param text the JSON text
     * @return the bean instance
     */
    T convert(String text);

    /**
     * Get format instructions with JSON schema.
     *
     * @return the format instructions
     */
    String getFormat();

    /**
     * Get the JSON schema for the target type.
     *
     * @return the JSON schema string
     */
    String getJsonSchema();

    /**
     * Get the JSON schema as a map.
     *
     * @return the schema map
     */
    Map<String, Object> getJsonSchemaMap();
}

ListOutputConverter

Converts text to lists of strings.

public class ListOutputConverter extends AbstractConversionServiceOutputConverter<List<String>> {
    /**
     * Construct a ListOutputConverter with ConversionService.
     *
     * @param conversionService the Spring ConversionService
     */
    public ListOutputConverter(ConversionService conversionService);

    /**
     * Convert text to list of strings.
     *
     * @param text the text (comma-separated, JSON array, etc.)
     * @return list of strings
     */
    List<String> convert(String text);
}

MapOutputConverter

Converts text to maps.

public class MapOutputConverter extends AbstractMessageOutputConverter<Map<String, Object>> {
    /**
     * Construct a MapOutputConverter.
     */
    public MapOutputConverter();

    /**
     * Convert text to map.
     *
     * @param text the JSON text
     * @return the map
     */
    Map<String, Object> convert(String text);
}

AbstractConversionServiceOutputConverter

Base class for converters using Spring's ConversionService.

public abstract class AbstractConversionServiceOutputConverter<T>
    implements StructuredOutputConverter<T> {
    // Base implementation with ConversionService support
}

AbstractMessageOutputConverter

Base class for message-based output converters.

public abstract class AbstractMessageOutputConverter<T>
    implements StructuredOutputConverter<T> {
    // Base implementation for message conversion
}

Response Text Cleaners

ResponseTextCleaner Interface

Cleans response text before conversion.

public interface ResponseTextCleaner {
    /**
     * Clean the response text.
     *
     * @param text the raw text
     * @return the cleaned text
     */
    String clean(String text);
}

MarkdownCodeBlockCleaner

Removes markdown code block delimiters.

public class MarkdownCodeBlockCleaner implements ResponseTextCleaner {
    /**
     * Remove markdown code blocks (```json, ```, etc.).
     *
     * @param text the text with markdown
     * @return the text without markdown delimiters
     */
    String clean(String text);
}

ThinkingTagCleaner

Removes thinking tags from responses.

public class ThinkingTagCleaner implements ResponseTextCleaner {
    /**
     * Remove <thinking> tags and their content.
     *
     * @param text the text with thinking tags
     * @return the text without thinking tags
     */
    String clean(String text);
}

WhitespaceCleaner

Trims whitespace from responses.

public class WhitespaceCleaner implements ResponseTextCleaner {
    /**
     * Trim leading and trailing whitespace.
     *
     * @param text the text
     * @return the trimmed text
     */
    String clean(String text);
}

CompositeResponseTextCleaner

Combines multiple cleaners.

public class CompositeResponseTextCleaner implements ResponseTextCleaner {
    /**
     * Construct a CompositeResponseTextCleaner.
     *
     * @param cleaners the list of cleaners to apply in order
     */
    public CompositeResponseTextCleaner(List<ResponseTextCleaner> cleaners);

    /**
     * Apply all cleaners in sequence.
     *
     * @param text the text to clean
     * @return the cleaned text
     */
    String clean(String text);

    /**
     * Create a new builder.
     *
     * @return a new builder
     */
    static Builder builder();
}

Usage Examples

Converting to POJOs

import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;

// Define target class
record Person(String name, int age, String occupation) {}

// Create converter
BeanOutputConverter<Person> converter = new BeanOutputConverter<>(Person.class);

// Create prompt with format instructions
String template = """
    Extract person information from this text: {text}

    {format}
    """;

PromptTemplate promptTemplate = new PromptTemplate(template);
promptTemplate.add("text", "John Smith is a 35-year-old software engineer");
promptTemplate.add("format", converter.getFormat());

Prompt prompt = promptTemplate.create();

// Get response
ChatResponse response = chatModel.call(prompt);
String responseText = response.getResult().getOutput().getText();

// Convert to POJO
Person person = converter.convert(responseText);
System.out.println(person.name());  // "John Smith"
System.out.println(person.age());   // 35

Complex Object Conversion

// Complex nested class
record Address(String street, String city, String country) {}
record Contact(String email, String phone) {}
record Employee(
    String name,
    Address address,
    Contact contact,
    List<String> skills
) {}

// Create converter
BeanOutputConverter<Employee> converter = new BeanOutputConverter<>(Employee.class);

// Use in prompt
String prompt = """
    Extract employee information and format as:
    %s
    """.formatted(converter.getFormat());

// Parse response
Employee employee = converter.convert(responseText);

Parameterized Types

import org.springframework.core.ParameterizedTypeReference;

// List of POJOs
ParameterizedTypeReference<List<Person>> listType =
    new ParameterizedTypeReference<>() {};

BeanOutputConverter<List<Person>> listConverter =
    new BeanOutputConverter<>(listType);

// Map of POJOs
ParameterizedTypeReference<Map<String, Person>> mapType =
    new ParameterizedTypeReference<>() {};

BeanOutputConverter<Map<String, Person>> mapConverter =
    new BeanOutputConverter<>(mapType);

// Use converters
List<Person> people = listConverter.convert(responseText);
Map<String, Person> personMap = mapConverter.convert(responseText);

List Conversion

import org.springframework.ai.converter.ListOutputConverter;

ListOutputConverter converter = new ListOutputConverter(conversionService);

// Convert comma-separated
String response1 = "apple, banana, orange";
List<String> fruits1 = converter.convert(response1);

// Convert JSON array
String response2 = "[\"apple\", \"banana\", \"orange\"]";
List<String> fruits2 = converter.convert(response2);

// Use in prompt
String prompt = "List 5 programming languages. " + converter.getFormat();

Map Conversion

import org.springframework.ai.converter.MapOutputConverter;

MapOutputConverter converter = new MapOutputConverter();

// Convert JSON object
String response = """
    {
        "name": "Alice",
        "age": 30,
        "city": "New York"
    }
    """;

Map<String, Object> data = converter.convert(response);
System.out.println(data.get("name"));  // "Alice"
System.out.println(data.get("age"));   // 30

Cleaning Response Text

import org.springframework.ai.converter.MarkdownCodeBlockCleaner;

MarkdownCodeBlockCleaner cleaner = new MarkdownCodeBlockCleaner();

// Input with markdown
String response = """
    ```json
    {
        "name": "John",
        "age": 30
    }
    ```
    """;

// Clean it
String cleaned = cleaner.clean(response);
// Result: {"name": "John", "age": 30}

// Then convert
BeanOutputConverter<Person> converter = new BeanOutputConverter<>(Person.class);
Person person = converter.convert(cleaned);

Composite Cleaning

import org.springframework.ai.converter.*;

// Build composite cleaner
CompositeResponseTextCleaner cleaner = CompositeResponseTextCleaner.builder()
    .add(new ThinkingTagCleaner())
    .add(new MarkdownCodeBlockCleaner())
    .add(new WhitespaceCleaner())
    .build();

// Clean complex response
String response = """
    <thinking>
    The user wants JSON format...
    </thinking>

    ```json
    {"name": "Bob", "age": 25}
    ```
    """;

String cleaned = cleaner.clean(response);
// Result: {"name": "Bob", "age": 25}

Custom Converter

public class CustomConverter implements StructuredOutputConverter<MyType> {
    @Override
    public MyType convert(String text) {
        // Custom parsing logic
        return parseMyType(text);
    }

    @Override
    public String getFormat() {
        return "Format your response as: field1=value1, field2=value2";
    }

    private MyType parseMyType(String text) {
        // Custom implementation
        String[] parts = text.split(",");
        // Parse and return MyType
        return new MyType(parts);
    }
}

Complete Example with Cleaning

@Service
public class StructuredOutputService {
    private final ChatModel chatModel;
    private final CompositeResponseTextCleaner cleaner;

    public StructuredOutputService(ChatModel chatModel) {
        this.chatModel = chatModel;
        this.cleaner = CompositeResponseTextCleaner.builder()
            .add(new ThinkingTagCleaner())
            .add(new MarkdownCodeBlockCleaner())
            .add(new WhitespaceCleaner())
            .build();
    }

    public <T> T extractStructured(String prompt, Class<T> targetClass) {
        // Create converter
        BeanOutputConverter<T> converter = new BeanOutputConverter<>(targetClass);

        // Add format instructions
        String fullPrompt = prompt + "\n\n" + converter.getFormat();

        // Get response
        String response = chatModel.call(fullPrompt);

        // Clean response
        String cleaned = cleaner.clean(response);

        // Convert to object
        return converter.convert(cleaned);
    }
}

JSON Schema Inspection

BeanOutputConverter<Person> converter = new BeanOutputConverter<>(Person.class);

// Get JSON schema as string
String schemaString = converter.getJsonSchema();
System.out.println(schemaString);

// Get JSON schema as map
Map<String, Object> schemaMap = converter.getJsonSchemaMap();
System.out.println(schemaMap.get("properties"));

// Use schema for validation or documentation

Error Handling

public <T> T safeConvert(String text, Class<T> clazz) {
    try {
        BeanOutputConverter<T> converter = new BeanOutputConverter<>(clazz);

        // Clean text first
        CompositeResponseTextCleaner cleaner = CompositeResponseTextCleaner.builder()
            .add(new MarkdownCodeBlockCleaner())
            .add(new WhitespaceCleaner())
            .build();

        String cleaned = cleaner.clean(text);

        // Convert
        return converter.convert(cleaned);
    } catch (Exception e) {
        System.err.println("Conversion failed: " + e.getMessage());
        return null;
    }
}

Enum Support

enum Status { ACTIVE, INACTIVE, PENDING }

record Task(
    String name,
    Status status,
    int priority
) {}

BeanOutputConverter<Task> converter = new BeanOutputConverter<>(Task.class);

// The schema will include enum constraints
String schema = converter.getJsonSchema();
// Will specify that status must be one of: ACTIVE, INACTIVE, PENDING

// Convert response
Task task = converter.convert(responseText);
System.out.println(task.status());  // Status.ACTIVE

Nested Collections

record Tag(String name, String color) {}
record Article(
    String title,
    String content,
    List<Tag> tags,
    Map<String, String> metadata
) {}

BeanOutputConverter<Article> converter = new BeanOutputConverter<>(Article.class);

// Schema will handle nested structure
String prompt = """
    Extract article information:
    %s
    """.formatted(converter.getFormat());

Article article = converter.convert(response);
System.out.println(article.tags().size());

Using with Prompt Templates

@Service
public class ExtractionService {
    private final ChatModel chatModel;

    public Person extractPerson(String text) {
        BeanOutputConverter<Person> converter = new BeanOutputConverter<>(Person.class);

        PromptTemplate template = new PromptTemplate("""
            Extract person information from this text: {input}

            Return your response in this format:
            {format}
            """);

        template.add("input", text);
        template.add("format", converter.getFormat());

        Prompt prompt = template.create();
        String response = chatModel.call(prompt)
            .getResult()
            .getOutput()
            .getText();

        return converter.convert(response);
    }
}

Custom ObjectMapper Configuration

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

// Configure ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// Use with converter
BeanOutputConverter<MyClass> converter =
    new BeanOutputConverter<>(MyClass.class, objectMapper);

// Now supports Java 8 date/time types and ignores unknown properties

Spring Integration

@Configuration
public class ConverterConfig {

    @Bean
    public CompositeResponseTextCleaner responseTextCleaner() {
        return CompositeResponseTextCleaner.builder()
            .add(new ThinkingTagCleaner())
            .add(new MarkdownCodeBlockCleaner())
            .add(new WhitespaceCleaner())
            .build();
    }

    @Bean
    public ObjectMapper converterObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        return mapper;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-ai--spring-ai-model@1.1.1

docs

index.md

tile.json