Core model interfaces and abstractions for Spring AI framework providing portable API for chat, embeddings, images, audio, and tool calling across multiple AI providers
Utility classes for JSON parsing and schema generation, tool utilities, usage calculators, document processing, and AOT/Native Image support for GraalVM compilation.
Utilities for parsing JSON to Java objects.
public class JsonParser {
/**
* Parse JSON to a specific class.
*
* @param json the JSON string
* @param clazz the target class
* @return the parsed object
*/
public static <T> T parse(String json, Class<T> clazz);
/**
* Parse JSON to a parameterized type.
*
* @param json the JSON string
* @param typeRef the parameterized type reference
* @return the parsed object
*/
public static <T> T parse(String json, ParameterizedTypeReference<T> typeRef);
/**
* Parse JSON to a map.
*
* @param json the JSON string
* @return the map
*/
public static Map<String, Object> parseMap(String json);
/**
* Parse JSON to a list.
*
* @param json the JSON string
* @return the list
*/
public static List<Object> parseList(String json);
}Generate JSON schemas from Java classes.
public class JsonSchemaGenerator {
/**
* Construct a JsonSchemaGenerator with default ObjectMapper.
*/
public JsonSchemaGenerator();
/**
* Construct a JsonSchemaGenerator with custom ObjectMapper.
*
* @param objectMapper the Jackson ObjectMapper
*/
public JsonSchemaGenerator(ObjectMapper objectMapper);
/**
* Generate JSON schema for a class.
*
* @param type the class to generate schema for
* @return the JSON schema string
*/
public String generate(Class<?> type);
/**
* Generate JSON schema for a parameterized type.
*
* @param type the parameterized type reference
* @return the JSON schema string
*/
public String generate(ParameterizedTypeReference<?> type);
/**
* Generate JSON schema as a map.
*
* @param type the class to generate schema for
* @return the schema map
*/
public Map<String, Object> generateMap(Class<?> type);
/**
* Generate JSON schema as a map for parameterized type.
*
* @param type the parameterized type reference
* @return the schema map
*/
public Map<String, Object> generateMap(ParameterizedTypeReference<?> type);
}Utility methods for JSON schema operations.
import org.springframework.ai.util.json.schema.JsonSchemaUtils;
public final class JsonSchemaUtils {
/**
* Ensures that the input schema is valid for AI model APIs. Many AI models require
* that the parameters object must have a "properties" field, even if it's empty. This
* method normalizes schemas from external sources (like MCP tools) that may not
* include this field.
*
* @param inputSchema the input schema as a JSON string
* @return a valid input schema as a JSON string with required fields
*/
public static String ensureValidInputSchema(String inputSchema);
}Jackson module for Spring AI schema generation with customization options.
public class SpringAiSchemaModule {
/**
* Options for schema generation behavior.
*/
public enum Option {
/**
* Include schema $id field.
*/
INCLUDE_SCHEMA_ID,
/**
* Include schema $schema field.
*/
INCLUDE_SCHEMA_VERSION,
/**
* Use draft 2020-12 schema version.
*/
USE_DRAFT_2020_12
}
}Schema generation options for customizing output.
public enum SchemaOption {
/**
* Include additional properties in schema.
*/
ALLOW_ADDITIONAL_PROPERTIES,
/**
* Require all properties by default.
*/
ALL_REQUIRED,
/**
* Include example values in schema.
*/
INCLUDE_EXAMPLES,
/**
* Use inline definitions instead of $defs.
*/
INLINE_DEFINITIONS
}Enumeration of JSON schema types.
public enum SchemaType {
OBJECT("object"),
ARRAY("array"),
STRING("string"),
NUMBER("number"),
INTEGER("integer"),
BOOLEAN("boolean"),
NULL("null");
/**
* Get the string value of this schema type.
*
* @return the type string
*/
public String getValue();
}Utility for creating tool callbacks from objects containing @Tool annotated methods.
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
public final class ToolCallbacks {
/**
* Creates tool callbacks from objects containing @Tool annotated methods.
* Internally uses MethodToolCallbackProvider to scan the provided objects
* for methods annotated with @Tool and wraps them as ToolCallback instances.
*
* @param sources one or more objects containing @Tool annotated methods
* @return array of tool callbacks for all discovered @Tool methods
*/
public static ToolCallback[] from(Object... sources);
}Usage Example:
// Define a class with @Tool annotated methods
public class WeatherTools {
@Tool(description = "Get current weather for a location")
public String getCurrentWeather(String location) {
return "Sunny, 72°F in " + location;
}
@Tool(description = "Get weather forecast")
public String getForecast(String location, int days) {
return days + "-day forecast for " + location;
}
}
// Create tool callbacks from the object
WeatherTools tools = new WeatherTools();
ToolCallback[] callbacks = ToolCallbacks.from(tools);
// Use with chat options
ChatOptions options = ChatOptions.builder()
.tools(callbacks)
.build();Utility class for manipulating ModelOptions objects, JSON conversion, and options merging.
import org.springframework.ai.model.ModelOptionsUtils;
public abstract class ModelOptionsUtils {
/**
* Shared ObjectMapper instance configured for Spring AI.
*/
public static final ObjectMapper OBJECT_MAPPER;
/**
* Converts the given JSON string to a Map of String and Object.
*
* @param json the JSON string to convert to a Map
* @return the converted Map
*/
public static Map<String, Object> jsonToMap(String json);
/**
* Converts the given JSON string to a Map using a custom ObjectMapper.
*
* @param json the JSON string to convert to a Map
* @param objectMapper the ObjectMapper to use for deserialization
* @return the converted Map
*/
public static Map<String, Object> jsonToMap(String json, ObjectMapper objectMapper);
/**
* Converts the given JSON string to an Object of the given type.
*
* @param <T> the type of the object to return
* @param json the JSON string to convert to an object
* @param type the type of the object to return
* @return Object instance of the given type
*/
public static <T> T jsonToObject(String json, Class<T> type);
/**
* Converts the given object to a JSON string.
*
* @param object the object to convert to a JSON string
* @return the JSON string
*/
public static String toJsonString(Object object);
/**
* Converts the given object to a pretty-printed JSON string.
*
* @param object the object to convert to a JSON string
* @return the pretty-printed JSON string
*/
public static String toJsonStringPrettyPrinter(Object object);
/**
* Merges the source object into the target object and returns an object represented
* by the given class. The JSON property names are used to match the fields to merge.
* The source non-null values override the target values with the same field name.
*
* @param <T> the type of the class to return
* @param source the source object to merge
* @param target the target object to merge into
* @param clazz the class to return
* @param acceptedFieldNames the list of field names accepted for the target object
* @return the merged object represented by the given class
*/
public static <T> T merge(Object source, Object target, Class<T> clazz, List<String> acceptedFieldNames);
/**
* Merges the source object into the target object using @JsonProperty field names.
*
* @param <T> the type of the class to return
* @param source the source object to merge
* @param target the target object to merge into
* @param clazz the class to return
* @return the merged object represented by the given class
*/
public static <T> T merge(Object source, Object target, Class<T> clazz);
/**
* Converts the given object to a Map.
*
* @param source the object to convert to a Map
* @return the converted Map
*/
public static Map<String, Object> objectToMap(Object source);
/**
* Converts the given Map to the given class.
*
* @param <T> the type of the class to return
* @param source the Map to convert to the given class
* @param clazz the class to convert the Map to
* @return the converted class
*/
public static <T> T mapToClass(Map<String, Object> source, Class<T> clazz);
/**
* Returns the list of name values of the @JsonProperty annotations.
*
* @param clazz the class that contains fields annotated with @JsonProperty
* @return the list of values of the @JsonProperty annotations
*/
public static List<String> getJsonPropertyValues(Class<?> clazz);
/**
* Returns a new instance of the targetBeanClazz that copies the bean values from the
* sourceBean instance.
*
* @param <I> the interface type
* @param <S> the source type extending the interface
* @param <T> the target type extending the source
* @param sourceBean the source bean to copy the values from
* @param sourceInterfaceClazz the source interface class
* @param targetBeanClazz the target class to convert into
* @return a new instance of the targetBeanClazz with the values from the sourceBean
*/
public static <I, S extends I, T extends S> T copyToTarget(S sourceBean, Class<I> sourceInterfaceClazz, Class<T> targetBeanClazz);
/**
* Merges the source object into the target object. The source null values are ignored.
*
* @param <I> the interface type
* @param <S> the source type extending the interface
* @param <T> the target type extending the source
* @param source the source object to merge
* @param target the target object to merge into
* @param sourceInterfaceClazz the source interface class
* @param overrideNonNullTargetValues if true, override non-null target values
* @return the merged target object
*/
public static <I, S extends I, T extends S> T mergeBeans(S source, T target, Class<I> sourceInterfaceClazz, boolean overrideNonNullTargetValues);
/**
* Generates JSON Schema (version 2020_12) for the given class.
*
* @param inputType the input Type to generate JSON Schema from
* @param toUpperCaseTypeValues if true, the type values are converted to upper case
* @return the generated JSON Schema as a String
*/
public static String getJsonSchema(Type inputType, boolean toUpperCaseTypeValues);
/**
* Return the runtime value if not empty, or else the default value.
*
* @param <T> the type of the value
* @param runtimeValue the runtime value
* @param defaultValue the default value
* @return the runtime value if not empty, otherwise the default value
*/
public static <T> T mergeOption(T runtimeValue, T defaultValue);
}Utility methods for embedding related operations and type conversions.
import org.springframework.ai.model.EmbeddingUtils;
public final class EmbeddingUtils {
/**
* Convert a list of Double values to Float values.
*
* @param doubles the list of Double values
* @return the list of Float values
*/
public static List<Float> doubleToFloat(List<Double> doubles);
/**
* Convert a List of Float objects to a primitive float array.
*
* @param floats the List of Float objects
* @return the primitive float array
*/
public static float[] toPrimitive(List<Float> floats);
/**
* Convert a Float array to a primitive float array.
*
* @param array the Float array
* @return the primitive float array
*/
public static float[] toPrimitive(Float[] array);
/**
* Convert a primitive float array to a Float array.
*
* @param array the primitive float array
* @return the Float array
*/
public static Float[] toFloatArray(float[] array);
/**
* Convert a primitive float array to a List of Float objects.
*
* @param floats the primitive float array
* @return the List of Float objects
*/
public static List<Float> toList(float[] floats);
}Constants for identifying AI model providers.
import org.springframework.ai.model.SpringAIModels;
public final class SpringAIModels {
public static final String ANTHROPIC = "anthropic";
public static final String AZURE_OPENAI = "azure-openai";
public static final String BEDROCK_COHERE = "bedrock-cohere";
public static final String BEDROCK_CONVERSE = "bedrock-converse";
public static final String BEDROCK_TITAN = "bedrock-titan";
public static final String DEEPSEEK = "deepseek";
public static final String ELEVEN_LABS = "elevenlabs";
public static final String GOOGLE_GEN_AI = "google-genai";
public static final String HUGGINGFACE = "huggingface";
public static final String MINIMAX = "minimax";
public static final String MISTRAL = "mistral";
public static final String OCI_GENAI = "oci-genai";
public static final String OLLAMA = "ollama";
public static final String OPENAI = "openai";
public static final String OPENAI_SDK = "openai-sdk";
public static final String POSTGRESML = "postgresml";
public static final String STABILITY_AI = "stabilityai";
public static final String TRANSFORMERS = "transformers";
public static final String VERTEX_AI = "vertexai";
public static final String ZHIPUAI = "zhipuai";
}Spring AI provides runtime hints for GraalVM Native Image compilation.
Interface for providing runtime hints.
public interface AiRuntimeHints {
/**
* Register runtime hints for reflection, resources, and proxies.
*
* @param hints the runtime hints to populate
* @param classLoader the class loader
*/
void registerHints(RuntimeHints hints, ClassLoader classLoader);
}Core runtime hints for Spring AI.
public class SpringAiCoreRuntimeHints implements AiRuntimeHints {
/**
* Registers core Spring AI classes, resources, and proxies
* for Native Image compilation.
*/
@Override
void registerHints(RuntimeHints hints, ClassLoader classLoader);
}Runtime hints for tool calling functionality.
public class ToolRuntimeHints implements AiRuntimeHints {
/**
* Registers tool-related classes for reflection
* including annotations and callback interfaces.
*/
@Override
void registerHints(RuntimeHints hints, ClassLoader classLoader);
}AOT processor for tool beans.
public class ToolBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
/**
* Processes tool beans at build time for AOT compilation.
*
* @param registeredBean the registered bean
* @param beanRegistrationCode the bean registration code
* @return the AOT contribution
*/
@Override
BeanRegistrationAotContribution processAheadOfTime(
RegisteredBean registeredBean,
BeanRegistrationCode beanRegistrationCode
);
}Runtime hints for Knuddels integration (if applicable).
public class KnuddelsRuntimeHints implements AiRuntimeHints {
/**
* Registers Knuddels-specific runtime hints.
*/
@Override
void registerHints(RuntimeHints hints, ClassLoader classLoader);
}Utility class for accumulating usage tokens across chat responses.
import org.springframework.ai.support.UsageCalculator;
import org.springframework.ai.chat.metadata.Usage;
import org.springframework.ai.chat.model.ChatResponse;
public final class UsageCalculator {
/**
* Accumulate usage tokens from the previous chat response to the current usage tokens.
* Returns the current usage if the previous response has no usage data.
*
* @param currentUsage the current usage
* @param previousChatResponse the previous chat response
* @return accumulated usage with combined token counts
*/
public static Usage getCumulativeUsage(final Usage currentUsage, final ChatResponse previousChatResponse);
/**
* Check if the Usage is empty.
* Returns true when the Usage is null or has zero total tokens.
*
* @param usage the usage to check
* @return true if usage is empty, false otherwise
*/
public static boolean isEmpty(Usage usage);
}Usage Example:
Usage currentUsage = chatResponse.getMetadata().getUsage();
Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousResponse);
System.out.println("Total tokens used: " + accumulatedUsage.getTotalTokens());Represents a document for processing and embedding.
public class Document {
/**
* Construct a Document with content.
*
* @param content the document content
*/
public Document(String content);
/**
* Construct a Document with content and metadata.
*
* @param content the document content
* @param metadata the document metadata
*/
public Document(String content, Map<String, Object> metadata);
/**
* Construct a Document with ID, content, and metadata.
*
* @param id the document ID
* @param content the document content
* @param metadata the document metadata
*/
public Document(String id, String content, Map<String, Object> metadata);
/**
* Get the document ID.
*
* @return the ID
*/
String getId();
/**
* Get the document text content.
*
* @return the text content
*/
String getText();
/**
* Get the document metadata.
*
* @return the metadata map
*/
Map<String, Object> getMetadata();
/**
* Get the media attachments.
*
* @return list of media
*/
List<Media> getMedia();
/**
* Get the embedding vector for this document.
*
* @return the embedding, or null if not set
*/
float[] getEmbedding();
/**
* Set the embedding vector for this document.
*
* @param embedding the embedding vector
*/
void setEmbedding(float[] embedding);
}Transform documents (e.g., add metadata, split, enrich).
public interface DocumentTransformer {
/**
* Transform a list of documents.
*
* @param documents the documents to transform
* @return the transformed documents
*/
List<Document> transform(List<Document> documents);
}Enriches documents with extracted keywords.
public class KeywordMetadataEnricher implements DocumentTransformer {
/**
* Construct a KeywordMetadataEnricher.
*
* @param chatModel the chat model for keyword extraction
* @param numberOfKeywords the number of keywords to extract
*/
public KeywordMetadataEnricher(ChatModel chatModel, int numberOfKeywords);
@Override
public List<Document> transform(List<Document> documents);
}Enriches documents with summaries.
public class SummaryMetadataEnricher implements DocumentTransformer {
/**
* Construct a SummaryMetadataEnricher.
*
* @param chatModel the chat model for summarization
*/
public SummaryMetadataEnricher(ChatModel chatModel);
@Override
public List<Document> transform(List<Document> documents);
}Runtime hints for GraalVM native image compilation.
public class AiRuntimeHints implements RuntimeHintsRegistrar {
/**
* Register reflection hints for Spring AI classes.
*
* @param hints the runtime hints registry
* @param classLoader the class loader
*/
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader);
}Core runtime hints for Spring AI.
public class SpringAiCoreRuntimeHints implements RuntimeHintsRegistrar {
/**
* Register core reflection hints.
*
* @param hints the runtime hints registry
* @param classLoader the class loader
*/
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader);
}Runtime hints for tool/function calling.
public class ToolRuntimeHints implements RuntimeHintsRegistrar {
/**
* Register tool reflection hints.
*
* @param hints the runtime hints registry
* @param classLoader the class loader
*/
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader);
}AOT processor for tool beans.
public class ToolBeanRegistrationAotProcessor {
/**
* Process tool beans at build time for AOT compilation.
*/
}import org.springframework.ai.util.json.JsonParser;
// Parse to class
String json = "{\"name\":\"John\",\"age\":30}";
Person person = JsonParser.parse(json, Person.class);
// Parse to parameterized type
String listJson = "[{\"name\":\"John\"},{\"name\":\"Jane\"}]";
List<Person> people = JsonParser.parse(
listJson,
new ParameterizedTypeReference<List<Person>>() {}
);
// Parse to map
Map<String, Object> map = JsonParser.parseMap(json);
String name = (String) map.get("name");
Integer age = (Integer) map.get("age");
// Parse to list
String arrayJson = "[1,2,3,4,5]";
List<Object> numbers = JsonParser.parseList(arrayJson);import org.springframework.ai.util.json.schema.JsonSchemaGenerator;
// Create generator
JsonSchemaGenerator generator = new JsonSchemaGenerator();
// Generate schema for class
record Product(String name, double price, List<String> tags) {}
String schema = generator.generate(Product.class);
System.out.println(schema);
// {
// "type": "object",
// "properties": {
// "name": {"type": "string"},
// "price": {"type": "number"},
// "tags": {"type": "array", "items": {"type": "string"}}
// },
// "required": ["name", "price", "tags"]
// }
// Generate as map
Map<String, Object> schemaMap = generator.generateMap(Product.class);import org.springframework.ai.support.ToolCallbacks;
// Create from lambda function
Function<String, String> upperCase = String::toUpperCase;
ToolCallback tool1 = ToolCallbacks.fromFunction(
"upper_case",
"Converts text to uppercase",
upperCase
);
// Create from method
public class MathTools {
public int add(int a, int b) {
return a + b;
}
}
MathTools mathTools = new MathTools();
Method addMethod = MathTools.class.getMethod("add", int.class, int.class);
ToolCallback tool2 = ToolCallbacks.fromMethod(mathTools, addMethod);
// Create from method name
ToolCallback tool3 = ToolCallbacks.fromMethod(mathTools, "add");import org.springframework.ai.support.UsageCalculator;
// Estimate tokens for text
String text = "This is a sample text for token estimation.";
int estimatedTokens = UsageCalculator.calculateUsage(text);
System.out.println("Estimated tokens: " + estimatedTokens);
// Estimate for multiple texts
List<String> texts = List.of(
"First text",
"Second text",
"Third text"
);
int totalTokens = UsageCalculator.calculateUsage(texts);
System.out.println("Total estimated tokens: " + totalTokens);
// Pre-check before API call
if (estimatedTokens > 4000) {
System.out.println("Text may exceed token limit, consider splitting");
}import org.springframework.ai.document.Document;
// Create document
Document doc = new Document("This is document content");
// Create with metadata
Map<String, Object> metadata = Map.of(
"source", "file.txt",
"author", "John Doe",
"timestamp", System.currentTimeMillis()
);
Document docWithMeta = new Document("Content", metadata);
// Create with ID
Document docWithId = new Document("doc_123", "Content", metadata);
// Add embedding
float[] embedding = embeddingModel.embed(doc.getText());
doc.setEmbedding(embedding);
// Access document properties
String id = doc.getId();
String content = doc.getText();
Map<String, Object> meta = doc.getMetadata();
float[] vector = doc.getEmbedding();import org.springframework.ai.model.transformer.*;
@Service
public class DocumentEnrichmentService {
private final ChatModel chatModel;
public List<Document> enrichDocuments(List<Document> documents) {
// Add keywords
KeywordMetadataEnricher keywordEnricher =
new KeywordMetadataEnricher(chatModel, 5);
documents = keywordEnricher.transform(documents);
// Add summaries
SummaryMetadataEnricher summaryEnricher =
new SummaryMetadataEnricher(chatModel);
documents = summaryEnricher.transform(documents);
return documents;
}
}public class LanguageDetectionTransformer implements DocumentTransformer {
@Override
public List<Document> transform(List<Document> documents) {
return documents.stream()
.map(doc -> {
// Detect language (simplified)
String language = detectLanguage(doc.getText());
// Add to metadata
Map<String, Object> metadata = new HashMap<>(doc.getMetadata());
metadata.put("language", language);
return new Document(doc.getId(), doc.getText(), metadata);
})
.toList();
}
private String detectLanguage(String content) {
// Language detection logic
return "en";
}
}import org.springframework.ai.util.json.schema.SchemaType;
// Use in schema generation
Map<String, Object> schema = new HashMap<>();
schema.put("type", SchemaType.OBJECT.getValue());
Map<String, Object> properties = new HashMap<>();
properties.put("name", Map.of("type", SchemaType.STRING.getValue()));
properties.put("age", Map.of("type", SchemaType.INTEGER.getValue()));
properties.put("active", Map.of("type", SchemaType.BOOLEAN.getValue()));
schema.put("properties", properties);JsonSchemaGenerator generator = new JsonSchemaGenerator();
// Nested types
record Address(String street, String city) {}
record Person(String name, int age, Address address, List<String> tags) {}
String schema = generator.generate(Person.class);
// Generates schema with nested object for Address and array for tags
// Generic types
ParameterizedTypeReference<Map<String, List<Person>>> complexType =
new ParameterizedTypeReference<>() {};
String complexSchema = generator.generate(complexType);import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
ObjectMapper mapper = new ObjectMapper();
// Configure mapper as needed
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
JsonSchemaGenerator generator = new JsonSchemaGenerator(mapper);@Service
public class DocumentPipeline {
private final List<DocumentTransformer> transformers;
public DocumentPipeline(
ChatModel chatModel,
EmbeddingModel embeddingModel
) {
this.transformers = List.of(
new KeywordMetadataEnricher(chatModel, 5),
new SummaryMetadataEnricher(chatModel),
new LanguageDetectionTransformer(),
new EmbeddingTransformer(embeddingModel)
);
}
public List<Document> process(List<Document> documents) {
List<Document> result = documents;
// Apply all transformers in sequence
for (DocumentTransformer transformer : transformers) {
result = transformer.transform(result);
}
return result;
}
}// In your application
@ImportRuntimeHints({
AiRuntimeHints.class,
SpringAiCoreRuntimeHints.class,
ToolRuntimeHints.class
})
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}@Service
public class ComprehensiveUtilityService {
private final ChatModel chatModel;
private final EmbeddingModel embeddingModel;
private final JsonSchemaGenerator schemaGenerator;
public void demonstrateUtilities() {
// JSON parsing
String json = "{\"name\":\"Test\"}";
Map<String, Object> data = JsonParser.parseMap(json);
// Schema generation
String schema = schemaGenerator.generate(MyClass.class);
// Usage estimation
int tokens = UsageCalculator.calculateUsage("Sample text");
// Document processing
Document doc = new Document("Content");
DocumentTransformer enricher = new SummaryMetadataEnricher(chatModel);
List<Document> enriched = enricher.transform(List.of(doc));
// Tool creation
ToolCallback tool = ToolCallbacks.fromFunction(
"process",
"Process data",
this::processData
);
}
private String processData(String input) {
return input.toUpperCase();
}
}