|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring AI 2.0.0! |
Upgrade Notes
Upgrading to 2.0.0
This section covers all breaking changes and migration steps when upgrading from Spring AI 1.1.x to 2.0.0.
Advisors
spring-ai-advisors-vector-store renamed to spring-ai-vector-store-advisor
The spring-ai-advisors-vector-store module has been renamed to spring-ai-vector-store-advisor to better align with the naming conventions of other Spring AI modules.
ToolSearchToolCallingAdvisor moved to spring-ai-tool-search-advisor
The ToolSearchToolCallingAdvisor class has been moved to a dedicated module and package:
-
Artifact:
spring-ai-tool-search-advisor(previously bundled inspring-ai-tool-search-tool) -
Package:
org.springframework.ai.chat.client.advisor.toolsearch(previouslyorg.springframework.ai.tool.toolsearch.advisor)
Update your dependency and import statements accordingly:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tool-search-advisor</artifactId>
</dependency>
New: ToolSearchToolCallingAdvisor Auto-Configuration and Starter
A Spring Boot auto-configuration (spring-ai-autoconfigure-tool-search-advisor) and matching starter (spring-ai-starter-tool-search-advisor) are now available.
When enabled, ToolSearchToolCallingAdvisor replaces the default ToolCallingAdvisor in the auto-configured ChatClient, limiting the tool definitions sent to the LLM per call to only the most relevant ones (via keyword or semantic search).
Opt in by adding the starter and setting the property:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-tool-search-advisor</artifactId>
</dependency>
spring.ai.chat.client.tool-search-advisor.enabled=true
# Choose the index type: regex (default), lucene, or vector
spring.ai.chat.client.tool-search-advisor.tool-index-type=regex
The regex index requires no additional dependencies.
The lucene index requires org.apache.lucene:lucene-core on the classpath.
The vector index requires a VectorStore bean (spring-ai-vector-store on the classpath).
Changed: Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER default value
Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER changed from Ordered.HIGHEST_PRECEDENCE + 1000 to Ordered.HIGHEST_PRECEDENCE + 200, placing memory advisors at their default order outside the ToolCallingAdvisor (HIGHEST_PRECEDENCE + 300).
The ToolCallingAdvisor now manages conversation history internally across tool-call iterations by default. The memory advisor only stores the final user/assistant exchange, never writing tool-call messages to the ChatMemoryRepository. This is the correct default because most repository implementations do not support those message types.
If you need memory inside the loop (e.g. with InMemoryChatMemoryRepository), explicitly set the advisor order above ToolCallingAdvisor.DEFAULT_ORDER and call .disableInternalConversationHistory():
var toolCallingAdvisor = ToolCallingAdvisor.builder()
.disableInternalConversationHistory()
.build();
var chatMemoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)
.advisorOrder(Ordered.HIGHEST_PRECEDENCE + 400)
.build();
The spring-ai-session community project provides a session-aware memory implementation that fully supports tool-call messages and can be safely used inside the tool-call loop with any backend. It is planned to replace ChatMemory in Spring AI 2.1. See the spring-ai-session documentation for details.
|
Tool Calling
Removed: internalToolExecutionEnabled from ToolCallingChatOptions
The internalToolExecutionEnabled option has been removed from ToolCallingChatOptions and all provider-specific ChatOptions classes (e.g. OpenAiChatOptions, AnthropicChatOptions). The corresponding Spring Boot configuration property spring.ai.<provider>.chat.internal-tool-execution-enabled has also been removed.
Impact
Code that sets .internalToolExecutionEnabled(false) to opt into user-controlled tool execution will no longer compile.
Code that sets .internalToolExecutionEnabled(true) (explicitly enabling the internal loop) will no longer compile, and the behaviour it implied no longer exists — per-model internal tool execution has been removed from all ChatModel implementations.
Migration
Remove all calls to .internalToolExecutionEnabled(…). The recommended approaches are:
-
ToolCallingAdvisorviaChatClient(recommended) — auto-registered when tools are present; no flag needed. -
User-controlled loop via
ChatModeldirectly — simply invokeChatModelwithoutToolCallingAdvisor. Tool calls will not be executed automatically; checkchatResponse.hasToolCalls()yourself and drive the loop withToolCallingManager.
// Before
ChatOptions options = ToolCallingChatOptions.builder()
.toolCallbacks(ToolCallbacks.from(new MyTools()))
.internalToolExecutionEnabled(false) // <-- remove this line
.build();
// After
ChatOptions options = ToolCallingChatOptions.builder()
.toolCallbacks(ToolCallbacks.from(new MyTools()))
.build();
Removed: ToolExecutionEligibilityPredicate and DefaultToolExecutionEligibilityPredicate
ToolExecutionEligibilityPredicate (previously deprecated since 2.0.0) and its default implementation DefaultToolExecutionEligibilityPredicate have been removed. These interfaces combined an options-based policy check (internalToolExecutionEnabled) with a response check (hasToolCalls()). Since the options-based flag is gone, there is no replacement at the ChatModel level.
New: ToolExecutionEligibilityChecker in ToolCallingAdvisor
ToolExecutionEligibilityChecker (Function<ChatResponse, Boolean>) can now be set on ToolCallingAdvisor.Builder to customize when the tool-call loop iterates. The default is chatResponse → chatResponse != null && chatResponse.hasToolCalls().
This is the extension point for provider-specific stop-reason logic (e.g. checking a finish reason in addition to tool-call presence):
ToolCallingAdvisor advisor = ToolCallingAdvisor.builder()
.toolExecutionEligibilityChecker(response ->
response != null && response.hasToolCalls()
&& !"stop".equals(response.getResult().getMetadata().getFinishReason()))
.build();
Spring Boot users can provide a ToolExecutionEligibilityChecker bean; it will be picked up automatically by the auto-configured ToolCallingAdvisor.Builder:
@Bean
ToolExecutionEligibilityChecker myChecker() {
return response -> response != null && response.hasToolCalls();
}
New: spring.ai.chat.client.tool-calling.enabled Property
A new spring.ai.chat.client.tool-calling.enabled property (default true) controls whether the ToolCallingAdvisor is auto-registered by the auto-configured ChatClient.
Set it to false to disable automatic tool execution for all calls — tools are still sent to the AI model as definitions, but tool call responses are not executed automatically.
spring.ai.chat.client.tool-calling.enabled=false
This is a global alternative to using AdvisorParams.toolCallingAdvisorAutoRegister(false) on every individual call.
Removed: streamToolCallResponses from Advisor Builders and Auto-Configuration
The streamToolCallResponses option has been removed from ToolCallingAdvisor.Builder, ToolCallAdvisor.Builder, and ToolSearchToolCallingAdvisor.Builder, as well as from their corresponding Spring Boot auto-configuration properties:
-
spring.ai.chat.client.tool-calling.stream-tool-call-responses -
spring.ai.chat.client.tool-search-advisor.stream-tool-call-responses
Why
When streamToolCallResponses=true, intermediate tool-call request chunks were streamed downstream but the paired ToolResponseMessage (sent back to the LLM) was not.
Any downstream memory advisor that recorded those chunks would receive a tool-call request without the corresponding tool response, producing a corrupted conversation history.
The design flaw cannot be fixed without introducing a breaking change to ChatClientResponse, so the option has been removed entirely.
Impact
-
Any code that calls
.streamToolCallResponses(true)or.streamToolCallResponses(false)on an advisor builder will fail to compile. -
Any
application.propertiesorapplication.ymlentry for the removed properties is silently ignored.
Migration
Remove all calls to .streamToolCallResponses(…) from your builder chains:
// Before
var advisor = ToolCallingAdvisor.builder()
.streamToolCallResponses(true) // <-- remove this line
.build();
// After
var advisor = ToolCallingAdvisor.builder().build();
To gain visibility into each iteration of the tool-calling loop — the use case that streamToolCallResponses=true was intended to address — opt out of the auto-registered advisor and drive the loop manually using AdvisorParams.toolCallingAdvisorAutoRegister(false):
ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();
ToolCallback[] tools = ToolCallbacks.from(new WeatherTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(tools)
.build();
String question = "What is the weather in Amsterdam and Paris?";
Prompt prompt = new Prompt(List.of(new UserMessage(question)), chatOptions);
ChatClientResponse response = chatClient.prompt()
.user(question)
.options(chatOptions)
.advisors(AdvisorParams.toolCallingAdvisorAutoRegister(false))
.call()
.chatClientResponse();
while (response.chatResponse() != null && response.chatResponse().hasToolCalls()) {
// inspect or forward the tool-call chunk here before executing
ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, response.chatResponse());
prompt = new Prompt(result.conversationHistory(), chatOptions);
response = chatClient.prompt()
.messages(result.conversationHistory())
.options(chatOptions)
.advisors(AdvisorParams.toolCallingAdvisorAutoRegister(false))
.call()
.chatClientResponse();
}
// forward or process the final answer here
For a complete example including the streaming path, see User-Controlled Tool Execution — With ChatClient.
JDBC Chat Memory sequence_id Column
The JdbcChatMemoryRepository schema adds a sequence_id BIGINT column that determines message ordering within a conversation. Previously, ordering relied on the timestamp column, but TIMESTAMP precision differs across databases — on MySQL and MariaDB the default precision is one second, so messages saved within the same second were returned in a non-deterministic order. A dedicated integer sequence orders identically across every supported database.
The timestamp column is retained. It now stores the message creation time and is exposed in the message metadata under the JdbcChatMemoryRepository.CONVERSATION_TS key (a java.time.Instant), so applications can display when a message was created. The timestamp is preserved across saves: re-saving a conversation keeps each existing message’s original creation time.
Impact
-
The
SPRING_AI_CHAT_MEMORYtable gains asequence_idcolumn (BIGINT, orNUMBER(19)on Oracle andINTEGERon SQLite) and aSPRING_AI_CHAT_MEMORY_CONVERSATION_ID_SEQUENCE_ID_IDXindex. Thetimestampcolumn and its index are unchanged. -
Existing tables created by Spring AI 1.x require the new column before they will work, since messages are now ordered by
sequence_id. -
Messages read from the repository now carry their creation timestamp in metadata. Because the metadata differs, a retrieved message is no longer equal (
Message.equals) to an otherwise-identical message constructed in code. Code that compares messages by value, or relies on message identity in sets or maps, should account for this.
Migration
The change is additive: add the new column and backfill it from the existing per-conversation order. No data is lost and the timestamp column is untouched. For PostgreSQL:
ALTER TABLE SPRING_AI_CHAT_MEMORY ADD COLUMN sequence_id BIGINT;
WITH ordered AS (
SELECT ctid, ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY "timestamp") - 1 AS seq
FROM SPRING_AI_CHAT_MEMORY
)
UPDATE SPRING_AI_CHAT_MEMORY t
SET sequence_id = o.seq
FROM ordered o
WHERE t.ctid = o.ctid;
ALTER TABLE SPRING_AI_CHAT_MEMORY ALTER COLUMN sequence_id SET NOT NULL;
CREATE INDEX SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_SEQUENCE_ID_IDX
ON SPRING_AI_CHAT_MEMORY(conversation_id, sequence_id);
Adapt the identifier quoting and the row-identity expression (ctid above) to your database. The ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY <timestamp>) pattern reproduces the existing per-conversation order on every supported engine. Alternatively, since chat memory holds recent conversation context rather than permanent history, you can drop and recreate the table from the updated schema-<platform>.sql script.
Options Immutability and Default Values
Strict Immutability for Options
Options classes (like ChatOptions, EmbeddingOptions, etc.) are now strictly immutable.
Collections within options (like toolCallbacks, stopSequences, customHeaders) are now stored as unmodifiable collections.
Additionally, nullable collections are now used instead of empty ones to better represent the absence of a value.
Default Values Moved to Options Constructors
Default values for options (like default model names, temperatures, etc.) have been moved from the Model implementations and *Properties configuration classes to the options constructors themselves.
Default configuration properties in *Properties classes have been removed as they are no longer needed and should be consistent with the options-level defaults.
Configuration Properties Flattening
Building on the chat configuration changes described above, the configuration properties for all other models (Embedding, Image, Audio, Moderation, OCR, etc.) no longer use the .options prefix.
For example, the former spring.ai.openai.embedding.options.model is now spring.ai.openai.embedding.model.
Deprecated configuration properties are provided for the .options variants to ensure a smoother migration experience.
Impact
-
The
getOptions()method in*Propertiesclasses is deprecated. -
The nested
Optionsclasses within*Propertiesare deprecated. -
The
toOptions()method has been moved from the nestedOptionsclass to the root*Propertiesclass.
Migration
Update your application.properties or application.yml to remove the .options segment from the property keys.
# Before
spring.ai.openai.embedding.options.model=text-embedding-3-small
# After
spring.ai.openai.embedding.model=text-embedding-3-small
In Java code, use the root properties class directly to access options or call toOptions():
// Before
String model = properties.getOptions().getModel();
OpenAiEmbeddingOptions options = properties.getOptions().toOptions();
// After
String model = properties.getModel();
OpenAiEmbeddingOptions options = properties.toOptions();
Ollama
Renamed: spring.ai.ollama.chat.think-option to spring.ai.ollama.chat.think
The spring.ai.ollama.chat.think-option configuration property has been renamed to spring.ai.ollama.chat.think.
Update your application.properties or application.yml accordingly:
# Before
spring.ai.ollama.chat.think-option=true
# After
spring.ai.ollama.chat.think=true
Minimax dedicated support superseded by Anthropic support
Minimax dedicated support has been removed in favor of using Anthropic support as recommended by Minimax themselves.
Please use the Spring AI Anthropic support instead, configuring it with the https://api.minimax.io/anthropic base URL and MiniMax models.
Embeddings are no longer supported.
JSON Utilities Refactoring
New: JsonHelper and Updated JacksonUtils
A new JsonHelper class has been introduced in spring-ai-commons as the canonical way to perform JSON serialization and deserialization.
It is instantiable and accepts a custom JsonMapper, making it straightforward to customize JSON behavior per use case.
JacksonUtils gains a new getDefaultJsonMapper() static method that returns a shared, pre-configured JsonMapper instance.
JsonHelper centralizes JSON operations previously spread across JsonParser, ModelOptionsUtils, and McpJsonParser.
The default constructor uses the shared mapper from JacksonUtils.getDefaultJsonMapper().
Inject a custom JsonMapper if you need different serialization settings:
// Default — uses the shared JsonMapper from JacksonUtils
JsonHelper jsonHelper = new JsonHelper();
// Custom — supply your own JsonMapper
JsonMapper myMapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();
JsonHelper customHelper = new JsonHelper(myMapper);
Deprecated: JsonParser
JsonParser (in org.springframework.ai.util.json) is deprecated for removal. All methods now delegate to JsonHelper.
Migration
| Before | After |
|---|---|
|
|
|
|
|
|
|
|
|
|
Removed: JSON Methods from ModelOptionsUtils
The following members of ModelOptionsUtils have been removed, since model options no longer depend on Jackson directly.
| Removed member | Replacement |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Migration
// Before
import org.springframework.ai.model.ModelOptionsUtils;
Map<String, Object> map = ModelOptionsUtils.jsonToMap(jsonString);
String json = ModelOptionsUtils.toJsonString(myObject);
JsonMapper mapper = ModelOptionsUtils.JSON_MAPPER;
// After
import org.springframework.ai.util.JsonHelper;
import org.springframework.ai.util.JacksonUtils;
private static final JsonHelper jsonHelper = new JsonHelper();
Map<String, Object> map = jsonHelper.fromJsonToMap(jsonString);
String json = jsonHelper.toJson(myObject);
JsonMapper mapper = JacksonUtils.getDefaultJsonMapper();
MCP Elicitation API: TypeReference Replaced by ParameterizedTypeReference
The elicit(…) overloads that previously accepted tools.jackson.core.type.TypeReference<T> in McpAsyncRequestContext and McpSyncRequestContext now accept org.springframework.core.ParameterizedTypeReference<T>.
Impact
Any code that calls these methods with a Jackson TypeReference will fail to compile.
Affected method signatures:
-
McpAsyncRequestContext.elicit(TypeReference<T>) -
McpAsyncRequestContext.elicit(Consumer<ElicitationSpec>, TypeReference<T>) -
McpSyncRequestContext.elicit(TypeReference<T>) -
McpSyncRequestContext.elicit(Consumer<ElicitationSpec>, TypeReference<T>)
Migration
Replace the Jackson TypeReference anonymous class with a Spring ParameterizedTypeReference anonymous class:
// Before
import tools.jackson.core.type.TypeReference;
Mono<StructuredElicitResult<Map<String, Object>>> result =
context.elicit(e -> e.message("Please fill in the form"),
new TypeReference<Map<String, Object>>() {});
// After
import org.springframework.core.ParameterizedTypeReference;
Mono<StructuredElicitResult<Map<String, Object>>> result =
context.elicit(e -> e.message("Please fill in the form"),
new ParameterizedTypeReference<Map<String, Object>>() {});
The same change applies to McpSyncRequestContext:
// Before
StructuredElicitResult<Person> result =
context.elicit(e -> e.message("Provide your details"),
new TypeReference<Person>() {});
// After
StructuredElicitResult<Person> result =
context.elicit(e -> e.message("Provide your details"),
new ParameterizedTypeReference<Person>() {});
Google GenAI Embedding: GoogleGenAiEmbeddingConnectionDetails Package Change
GoogleGenAiEmbeddingConnectionDetails has been moved from org.springframework.ai.google.genai
to org.springframework.ai.google.genai.embedding.
ChatClient Tool Calling
Automatic ToolCallingAdvisor Registration
ChatClient now always auto-registers a ToolCallingAdvisor in the advisor chain (unless explicitly disabled), so tool calls requested by the model are handled automatically regardless of whether tools were configured statically or injected by another advisor at runtime.
Impact
Existing code that already adds a ToolCallingAdvisor explicitly will have the advisor present in the chain twice — once from the explicit add and once from auto-registration.
Migration
Remove the explicit ToolCallingAdvisor from the chain and let auto-registration handle it. If you need a custom configuration, either supply a pre-configured ToolCallingAdvisor.Builder at ChatClient construction time (see the Customizing the Default ToolCallingAdvisor section), or disable auto-registration and register the advisor manually:
// Before — manual registration
chatClient.prompt("What's the weather?")
.tools(weatherTool)
.advisors(ToolCallingAdvisor.builder().build())
.call().content();
// After — auto-registration handles it; no explicit advisor needed
chatClient.prompt("What's the weather?")
.tools(weatherTool)
.call().content();
// Or, to keep full control, disable auto-registration explicitly
chatClient.prompt("What's the weather?")
.tools(weatherTool)
.advisors(
AdvisorParams.toolCallingAdvisorAutoRegister(false),
ToolCallingAdvisor.builder().build()
)
.call().content();
New: ToolAdvisor Marker Interface
A ToolAdvisor marker interface has been added. ToolCallingAdvisor implements it. DefaultChatClient checks for this marker to decide whether to auto-register a ToolCallingAdvisor — if any advisor in the chain already implements ToolAdvisor, auto-registration is skipped.
Custom advisors that own the tool-call lifecycle should implement ToolAdvisor to prevent a duplicate ToolCallingAdvisor from being added automatically.
New: MemoryAdvisor Marker Interface
A MemoryAdvisor marker interface has been added. BaseChatMemoryAdvisor now extends it. DefaultChatClient uses this marker to detect downstream memory advisors and disable the `ToolCallingAdvisor’s internal conversation history when one is present.
Custom memory advisors that do not extend BaseChatMemoryAdvisor but should integrate with the tool-call auto-registration logic should implement MemoryAdvisor.
New: ChatClient.builder() Overload for Custom ToolCallingAdvisor Configuration
A new 5-argument ChatClient.builder() static method and a matching DefaultChatClientBuilder constructor accept a ToolCallingAdvisor.Builder<?> to configure the advisor used during auto-registration:
ChatClient chatClient = ChatClient
.builder(chatModel, observationRegistry, null, null,
ToolCallingAdvisor.builder().toolCallingManager(myToolCallingManager))
.build();
Spring Boot users should prefer one of the following instead:
-
Set
spring.ai.chat.client.tool-calling.advisor-orderto control the advisor’s position in the chain. -
Declare a
ToolCallingAdvisor.Builder<?>bean to fully replace the auto-configured builder (suppressed via@ConditionalOnMissingBean).
Removed: ToolSpec Consumer API on ChatClient
The tools(Consumer<ToolSpec>) / defaultTools(Consumer<ToolSpec>) API and the ChatClient.ToolSpec interface have been removed. The tools(Object…) / defaultTools(Object…) methods now directly accept ToolCallback, ToolCallbackProvider, @Tool-annotated POJO instances, and collections or arrays of any of these types. Context is set separately via the dedicated toolContext() / defaultToolContext() methods.
Migration
// Before
chatClient.prompt()
.tools(t -> t.callbacks(myCallback).context("tenantId", "acme"))
.call().content();
// After
chatClient.prompt()
.tools(myCallback)
.toolContext(Map.of("tenantId", "acme"))
.call().content();
The individual toolCallbacks() methods on ChatClientRequestSpec and defaultToolCallbacks() on ChatClient.Builder are deprecated in favour of tools(Object…) / defaultTools(Object…).
Changed: MethodToolCallbackProvider throws IllegalArgumentException instead of IllegalStateException
MethodToolCallbackProvider now throws IllegalArgumentException (instead of IllegalStateException) in two cases:
-
When a tool object has no
@Tool-annotated methods. -
When multiple tool objects produce callbacks with duplicate names.
Impact
Code that catches IllegalStateException from MethodToolCallbackProvider (directly or via ToolCallbacks.from()) must be updated.
// Before
try {
ToolCallbacks.from(myObject);
}
catch (IllegalStateException e) { ... }
// After
try {
ToolCallbacks.from(myObject);
}
catch (IllegalArgumentException e) { ... }
Removed: Spring Bean Tool Resolution (SpringBeanToolCallbackResolver)
SpringBeanToolCallbackResolver and the pattern of declaring bare Function / Supplier / Consumer beans and referencing them by name via toolNames() have been removed. The toolNames() method has been removed from all chat options classes and from ChatClient.
Migration
Declare ToolCallback beans directly instead of raw functional beans:
// Before — bare Function bean resolved by name at runtime
@Bean
@Description("Get the weather in location")
Function<WeatherRequest, WeatherResponse> currentWeather() {
return weatherService::getWeather;
}
// After — explicit ToolCallback bean
@Bean
ToolCallback currentWeather() {
return FunctionToolCallback.builder("currentWeather", weatherService::getWeather)
.description("Get the weather in location")
.inputType(WeatherRequest.class)
.build();
}
Then register the bean directly with tools():
chatClient.prompt("What's the weather like in Copenhagen?")
.tools(currentWeather)
.call().content();
Alternatively, use @Tool-annotated methods for the declarative approach (see Tools API).
Cloud Bindings
The spring-ai-spring-cloud-bindings module that provided integration for github.com/spring-cloud/spring-cloud-bindings has been removed.
BeanOutputConverter JSON Schema Generation
BeanOutputConverter now delegates JSON Schema generation to JsonSchemaGenerator, aligning structured output conversion with the JSON Schema behavior used for tool calling.
Impact
-
Kotlin properties that are optional in their primary constructor (nullable or declared with a default value) are no longer included in the JSON Schema
requiredarray. -
Properties annotated with
@JsonProperty(required = false), including@JsonPropertydeclarations whererequiredis not specified, are no longer treated as required. -
Generated schemas now include OpenAPI-style
formathints for primitive types (e.g.,int32forint,int64forlong,date-timeforLocalDateTime), matching the format conventions used for tool calling. -
The
BeanOutputConverter.postProcessSchema(JsonNode)extension point has been removed. Custom subclasses overriding this method will fail to compile.
Migration
To customize JSON Schema generation, override BeanOutputConverter.generateSchema() instead. Subclasses can post-process the default schema by delegating to super.generateSchema().
// Before
class CustomConverter extends BeanOutputConverter<MyType> {
CustomConverter() {
super(MyType.class);
}
@Override
protected void postProcessSchema(JsonNode schema) {
// mutate schema
}
}
// After
class CustomConverter extends BeanOutputConverter<MyType> {
CustomConverter() {
super(MyType.class);
}
@Override
protected String generateSchema() {
String schema = super.generateSchema();
// post-process schema
return schema;
}
}
Azure Cosmos DB Support
The Azure Cosmos DB vector store (spring-ai-azure-cosmos-db-store) and chat memory repository (spring-ai-model-chat-memory-repository-cosmos-db) modules, along with their corresponding auto-configurations and starters, have been removed from the Spring AI project.
Azure Cosmos DB support is now available as an external module maintained by the Azure Cosmos DB team. See azurecosmosdb.github.io/spring-ai/docs/index.html for documentation and dependency coordinates.
MCP SDK Breaking Changes: Required Fields
The MCP SDK enforces previously optional fields as mandatory at construction time via Assert.notNull() in compact record constructors.
Breaking: CreateMessageResult — model now required
// Before
CreateMessageResult.builder()
.content(new TextContent(response))
.build();
// After
CreateMessageResult.builder(Role.ASSISTANT, response, modelHint)
.build();
Breaking: CreateMessageRequest — maxTokens now required
// Before
CreateMessageRequest.builder()
.messages(messages)
.build();
// After
CreateMessageRequest.builder(messages, 500)
.build();
Deprecated Builder APIs
The following no-arg constructors and builder() methods are deprecated in favour of factory methods that require mandatory arguments upfront:
| Deprecated | Replacement |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Observability
Tool Calling
The observations produced for tool calling operations have changed as follows:
-
the span name is
execute_tool <tool-name>instead oftool_call <tool-name>. -
the metric/span attribute
gen_ai.operation.namehas valueexecute_toolinstead offramework. -
a new
spring.ai.tool.typemetric/span attribute has been introduced to capture the tool type (e.g.function). -
a new
spring.ai.tool.call.idspan attribute has been introduced to capture the tool call ID, as identified by the chat model.
Chat Memory Advisors: Conversation ID Is Now Required
The conversation ID is no longer optional for the built-in memory advisors (MessageChatMemoryAdvisor and VectorStoreChatMemoryAdvisor).
Every call through these advisors must supply ChatMemory.CONVERSATION_ID via the advisor context.
If the value is absent or null, the advisor throws an IllegalArgumentException immediately.
Removed: ChatMemory.DEFAULT_CONVERSATION_ID
The constant ChatMemory.DEFAULT_CONVERSATION_ID (value "default") has been removed from the ChatMemory interface.
Removed: .conversationId() Builder Method on Memory Advisors
The .conversationId(String) builder method has been removed from MessageChatMemoryAdvisor.Builder and VectorStoreChatMemoryAdvisor.Builder.
Setting a default conversation ID at construction time is no longer supported.
Migration
Remove the .conversationId() builder call and always supply the conversation ID at call time via the advisor context using ChatMemory.CONVERSATION_ID:
// Before
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory)
.conversationId("my-session")
.build())
.build();
chatClient.prompt()
.user("Hello!")
.call()
.content();
// After
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
chatClient.prompt()
.user("Hello!")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, "my-session"))
.call()
.content();
Changed: BaseChatMemoryAdvisor.getConversationId() Signature
The default method BaseChatMemoryAdvisor.getConversationId(Map, String) has been replaced by getConversationId(Map).
Impact
Custom advisor implementations that override or call the two-argument form will fail to compile.
Migration
Update overrides and call sites to the single-argument form. Ensure the context always contains ChatMemory.CONVERSATION_ID before calling the method.
// Before
String conversationId = getConversationId(context, this.defaultConversationId);
// After
String conversationId = getConversationId(context); // throws if CONVERSATION_ID is absent
Removed: PromptChatMemoryAdvisor
PromptChatMemoryAdvisor has been removed. Use MessageChatMemoryAdvisor as a replacement.
Migration
Replace PromptChatMemoryAdvisor with MessageChatMemoryAdvisor. The builder API is identical.
Instead of injecting memory as plain text into the system prompt, MessageChatMemoryAdvisor includes the conversation history directly as chat messages in the prompt.
// Before
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())
.build();
// After
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
ChatClient Options Now Require a Builder
When using ChatClient, the .options() / .defaultOptions() methods now accept a ChatOptions.Builder (or any provider-specific builder subtype) rather than a fully built ChatOptions instance.
This change is enforced at compile time by the method’s generic type constraint <B extends ChatOptions.Builder<?>>.
The options builder is merged with the model’s default options before the first advisor is called, so only the fields you explicitly set override the defaults.
// Before — passing a built ChatOptions instance (no longer compiles with ChatClient)
ChatOptions opts = AnthropicChatOptions.builder()
.maxTokens(100)
.temperature(0.7)
.build();
String response = chatClient.prompt("Tell me a joke")
.options(opts)
.call().content();
// After — pass the builder directly
String response = chatClient.prompt("Tell me a joke")
.options(AnthropicChatOptions.builder()
.maxTokens(100)
.temperature(0.7))
.call().content();
This restriction applies to ChatClient only. When calling ChatModel.call(Prompt) directly, the prompt must carry a fully built ChatOptions instance of the concrete type expected by the model (e.g. AnthropicChatOptions for AnthropicChatModel). The ChatModel does not merge options: if Prompt.getOptions() is non-null it is used as-is; if it is null, the model’s own default options are used instead. No partial merging occurs at the ChatModel level.
|
OpenAI Java SDK Transition
Spring AI now uses the official openai-java SDK under the hood for all OpenAI models (Chat, Embeddings, Image, Audio Speech, Audio Transcription, and Moderation) in the spring-ai-openai module.
The transition should be seamless, and no major breaking changes are expected for existing users of the spring-ai-openai module.
All properties (with the spring.ai.openai.* prefix), builders, and options remain fully intact. The existing extraBody configuration parameter transparently maps to the underlying additionalBodyProperties in the openai-java SDK.
spring-ai-azure-openai module removal
The spring-ai-azure-openai module (and its associated Spring Boot starter spring-ai-starter-model-azure-openai and auto-configuration spring-ai-autoconfigure-model-azure-openai) have been removed from Spring AI.
Existing users should use the spring-ai-openai module (and related starter and auto-configuration) instead and adapt class names (by removing the Azure prefix for example) and configuration properties accordingly.
spring-ai-openai-sdk module removal
The spring-ai-openai-sdk module (and its associated Spring Boot starter spring-ai-starter-model-openai-sdk and auto-configuration spring-ai-autoconfigure-model-openai-sdk) have been removed from Spring AI.
Existing users should use the spring-ai-openai module (and related starter and auto-configuration) instead and adapt class names accordingly (by removing the Sdk suffix for example) and configuration properties accordingly.
spring-ai-oci-genai module removal
The related modules can now be found at github.com/oracle/spring-cloud-oracle/tree/main/spring-ai-oracle.
MCP Java SDK Upgraded to 2.0.0
Spring AI 2.0.0 upgrades the MCP Java SDK from 1.1.x to 2.0.0. This release introduces several breaking changes at the SDK level that may affect applications that interact with MCP types directly.
Server-Side Tool Input Validation Enabled by Default
MCP servers now validate incoming tool arguments against the tool’s JSON schema before invoking the tool handler.
Failed validation produces a CallToolResult with isError=true and a descriptive error message.
Impact
Existing MCP servers that previously accepted loosely-typed or missing tool arguments may now return validation errors to clients that send non-conforming arguments.
Migration
Ensure that your tool definitions carry accurate JSON schemas and that all clients send conforming arguments.
To disable validation and restore the pre-2.0 behavior, set validateToolInputs(false) on the server builder:
McpServer.sync(transportProvider)
.validateToolInputs(false)
.tool(myTool, handler)
.build();
When using @McpTool annotations, Spring AI generates JSON schemas from method parameters automatically. These schemas are compatible with the built-in validator and require no extra action.
|
Tool.inputSchema Changed from JsonSchema to Map<String, Object>
McpSchema.Tool.inputSchema() (and outputSchema()) now returns Map<String, Object> instead of the former JsonSchema record.
This allows arbitrary JSON Schema dialect keywords ($ref, unevaluatedProperties, vendor extensions) to round-trip without being trimmed.
Migration
Switch to Map<String, Object>:
// Before
McpSchema.JsonSchema schema = tool.inputSchema();
// After
Map<String, Object> schema = tool.inputSchema();
When constructing tools via McpSchema.Tool.Builder, the deprecated inputSchema(JsonSchema) helper is still available for backwards compatibility, but prefer inputSchema(Map) or inputSchema(McpJsonMapper, String).
Applications that use @McpTool annotations or Spring AI’s SyncMcpToolProvider / AsyncMcpToolProvider are not affected — Spring AI handles schema generation internally.
|
Builder.customizeRequest() Removed from HTTP Client Transports
The deprecated Builder.customizeRequest() method has been removed from HttpClientSseClientTransport.Builder and HttpClientStreamableHttpTransport.Builder.
Migration
Replace customizeRequest() with httpRequestCustomizer() (sync) or asyncHttpRequestCustomizer() (async):
// Before
HttpClientSseClientTransport.builder(baseUrl)
.customizeRequest(req -> req.header("Authorization", "Bearer token"))
.build();
// After
HttpClientSseClientTransport.builder(baseUrl)
.httpRequestCustomizer(req -> req.header("Authorization", "Bearer token"))
.build();
If you already migrated to the McpClientCustomizer<B> API, no further action is required.
|
Sealed MCP Schema Interfaces Removed
The following interfaces are no longer sealed:
McpSchema.JSONRPCMessage, McpSchema.Request, McpSchema.Result, McpSchema.Notification,
McpSchema.ResourceContents, McpSchema.CompleteReference, and McpSchema.Content.
Impact
Exhaustive switch expressions over these types that relied on the sealed hierarchy for completeness no longer compile without a default branch.
Migration
Add a default branch to any exhaustive switch over these types:
// Before (no default needed when McpSchema.Content was sealed)
String text = switch (content) {
case McpSchema.TextContent tc -> tc.text();
case McpSchema.ImageContent ic -> "[image]";
case McpSchema.EmbeddedResource er -> "[resource]";
};
// After
String text = switch (content) {
case McpSchema.TextContent tc -> tc.text();
case McpSchema.ImageContent ic -> "[image]";
case McpSchema.EmbeddedResource er -> "[resource]";
default -> throw new IllegalArgumentException("Unknown content type: " + content);
};
| This change is unlikely to affect most Spring AI users who interact with MCP through annotations or Spring AI’s higher-level abstractions. |
Unified Cache Usage Metrics on Usage
The org.springframework.ai.chat.metadata.Usage interface gains two default methods, getCacheReadInputTokens() and getCacheWriteInputTokens(). Both return null when the provider doesn’t report a value.
Anthropic, Bedrock Converse, OpenAI, and Google GenAI populate them: Anthropic and Bedrock in both directions, OpenAI and Google for cache reads only.
Code that cast to native types or read provider-specific metadata keys for these numbers can switch to the interface methods. See Prompt Cache Usage Metrics for examples.
Anthropic Module
spring-ai-anthropic is now built on com.anthropic:anthropic-java instead of the hand-rolled RestClient / WebClient implementation.
For most applications the change is transparent. The Maven artifact, the spring.ai.anthropic.* configuration, and the AnthropicChatModel / AnthropicChatOptions / ChatClient API are preserved, and code that goes through ChatClient or ChatModel.call(Prompt) does not need updates.
Code that imported from org.springframework.ai.anthropic.api, constructed AnthropicChatModel with an AnthropicApi argument, or depended on the old maxTokens default does need attention.
Impact
-
org.springframework.ai.anthropic.api.AnthropicApiand all its nested record types (ChatCompletionRequest,ContentBlock,Tool,ToolChoice*, streaming event records) are gone. Code referencing these will not compile. -
The public
AnthropicChatModel(AnthropicApi, AnthropicChatOptions, …)constructor is gone. UseAnthropicChatModel.builder(). -
AnthropicChatOptions#maxTokensdefaults to4096instead of500. Responses that hit the old cap will now run longer, with correspondingly higher token usage. -
AnthropicCacheOptions,AnthropicCacheStrategy,AnthropicCacheTtl,CacheBreakpointTracker, andCacheEligibilityResolvermoved out ofapi(andapi.utils) into the rootorg.springframework.ai.anthropicpackage. -
AnthropicCacheType,StreamHelper, andmetadata.AnthropicRateLimitwere removed; the equivalent SDK types (CacheControlEphemeral,AsyncStreamResponse<RawMessageStreamEvent>,RateLimitException) are used directly. -
CitationDocumentwas renamed toAnthropicCitationDocument. -
com.anthropic:anthropic-javabringscom.squareup.okhttp3:okhttponto the classpath transitively.
Migration
Update imports for the moved cache and citation classes:
| Before | After |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
AnthropicCacheStrategy (NONE, TOOLS_ONLY, SYSTEM_ONLY, SYSTEM_AND_TOOLS, CONVERSATION_HISTORY) and AnthropicCacheTtl (FIVE_MINUTES, ONE_HOUR) keep the same enum values.
Replace direct constructor usage with the builder:
// Before
AnthropicApi anthropicApi = new AnthropicApi(apiKey);
AnthropicChatModel chatModel = new AnthropicChatModel(anthropicApi, options);
// After
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.apiKey(apiKey)
.defaultOptions(options)
.build();
If you relied on the 500-token default to bound costs, set it explicitly:
AnthropicChatOptions.builder()
.maxTokens(500)
// ...
.build();
Migrating the Anthropic Module to the Official Java SDK covers the rest: direct SDK access, streaming behavior, prompt caching, citations, and removed types.
New Chat Options
spring-ai-anthropic also gains four new chat options. The full reference for each is in Anthropic Chat; the highlights:
| Option | What it adds |
|---|---|
Thinking display |
|
Service tier |
|
Built-in web search |
|
Inference geo |
|
If you are upgrading from 1.1.x (or 1.0.x), see Migrating the Anthropic Module to the Official Java SDK for the full RestClient → SDK transition guide.
|
MCP Annotations Migrated into Spring AI
The org.springaicommunity:mcp-annotations external library has been removed as a dependency of the mcp-annotations module.
Its classes are now part of Spring AI itself under a new package structure.
Impact
-
All MCP annotation and provider classes have new fully-qualified names.
-
The
org.springaicommunity:mcp-annotationsartifact is no longer provided transitively by Spring AI. -
Any code importing from
org.springaicommunity.mcp.*will fail to compile.
Package Rename
| Old Package | New Package |
|---|---|
|
|
|
|
|
|
Migration
Update all imports in your application code:
// Before
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpPrompt;
import org.springaicommunity.mcp.annotation.McpResource;
import org.springaicommunity.mcp.annotation.McpSampling;
import org.springaicommunity.mcp.provider.tool.SyncMcpToolProvider;
import org.springaicommunity.mcp.provider.prompt.AsyncMcpPromptProvider;
// After
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpPrompt;
import org.springframework.ai.mcp.annotation.McpResource;
import org.springframework.ai.mcp.annotation.McpSampling;
import org.springframework.ai.mcp.annotation.provider.tool.SyncMcpToolProvider;
import org.springframework.ai.mcp.annotation.provider.prompt.AsyncMcpPromptProvider;
If you declared org.springaicommunity:mcp-annotations as a direct Maven or Gradle dependency, remove it — the classes are now provided by Spring AI’s spring-ai-mcp-annotations module.
Automated Migration with OpenRewrite
You can automate all import and dependency changes using the provided OpenRewrite recipe.
Apply the migrate-to-2-0-0-M3.yaml recipe from the command line:
mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
-Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
-Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpAnnotations \
-Dmaven.compiler.failOnError=false
The recipe performs two changes automatically:
-
Removes the
org.springaicommunity:mcp-annotationsMaven dependency. -
Rewrites all
importstatements across.javafiles to point to the neworg.springframework.ai.mcp.annotation.*packages.
To run all M3 migrations at once, use the umbrella recipe — see [run-all-m3-migrations].
MCP Spring Transport Modules Moved to Spring AI
The mcp-spring-webflux and mcp-spring-webmvc transport modules are no longer shipped by the MCP Java SDK. Starting with Spring AI 2.0, they are part of the Spring AI project itself.
Impact
-
The Maven group ID for both artifacts has changed.
-
Java package names for all Spring-specific transport classes have changed.
-
The MCP Java SDK version requirement has been bumped from
0.18.xto1.0.x.
Maven Dependency Group ID Change
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>mcp-spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>mcp-spring-webmvc</artifactId>
</dependency>
When using the spring-ai-bom or a Spring AI MCP starter (spring-ai-starter-mcp-server-webflux, spring-ai-starter-mcp-server-webmvc, spring-ai-starter-mcp-client-webflux), no explicit version is needed — the BOM manages it automatically.
|
Java Package Relocation
All Spring-specific transport classes have moved to org.springframework.ai packages.
| Class | Old package | New package |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Class | Old package | New package |
|---|---|---|
|
|
|
|
|
|
Migration
Update your Java imports:
// Before
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
// After
import org.springframework.ai.mcp.server.webflux.transport.WebFluxSseServerTransportProvider;
import org.springframework.ai.mcp.server.webmvc.transport.WebMvcSseServerTransportProvider;
import org.springframework.ai.mcp.client.webflux.transport.WebFluxSseClientTransport;
import org.springframework.ai.mcp.client.webflux.transport.WebClientStreamableHttpTransport;
If you rely exclusively on Spring Boot auto-configuration via the Spring AI starters, no Java code changes are required. Only update your pom.xml/build.gradle dependency coordinates as described above.
|
For the full MCP transport migration reference, see Upgrading to Spring AI 2.0.
Automated Migration with OpenRewrite
You can automate all Maven dependency and Java import changes using the provided OpenRewrite recipe:
mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
-Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
-Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpSpringTransports \
-Dmaven.compiler.failOnError=false
If your project declares io.modelcontextprotocol.sdk:mcp-spring-webflux or mcp-spring-webmvc without an explicit <version> (version managed via a BOM), Maven will refuse to parse the pom.xml and the recipe will never run.
Pre-patch those files first, then run OpenRewrite:
|
# Step 1 – patch the groupIds directly so Maven can load the modules
find . -name "pom.xml" -print0 \
| xargs -0 perl -i -0pe \
's{<groupId>io\.modelcontextprotocol\.sdk</groupId>(\s+)<artifactId>mcp-spring-webflux</artifactId>}{<groupId>org.springframework.ai</groupId>$1<artifactId>mcp-spring-webflux</artifactId>}g;
s{<groupId>io\.modelcontextprotocol\.sdk</groupId>(\s+)<artifactId>mcp-spring-webmvc</artifactId>}{<groupId>org.springframework.ai</groupId>$1<artifactId>mcp-spring-webmvc</artifactId>}g'
# Step 2 – run OpenRewrite to migrate Java imports and remaining POM changes
mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
-Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
-Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpSpringTransports \
-Dmaven.compiler.failOnError=false
To run all M3 migrations at once, use the umbrella recipe — see [run-all-m3-migrations].
MCP Client Customizer API Consolidated
McpAsyncClientCustomizer and McpSyncClientCustomizer have been removed and replaced by a single generic interface McpClientCustomizer<B>.
Impact
-
McpAsyncClientCustomizerno longer exists — compile error for any implementing bean. -
McpSyncClientCustomizerno longer exists — compile error for any implementing bean. -
McpSyncClientConfigurerandMcpAsyncClientConfigurerconstructors now acceptList<McpClientCustomizer<…>>instead of the old type-specific lists. -
In the HttpClient-based transport auto-configurations (
SseHttpClientTransportAutoConfiguration,StreamableHttpHttpClientTransportAutoConfiguration), the SDK-levelMcpSyncHttpClientRequestCustomizerandMcpAsyncHttpClientRequestCustomizerbeans are no longer applied. Transport-level customization now goes throughMcpClientCustomizer<HttpClientSseClientTransport.Builder>andMcpClientCustomizer<HttpClientStreamableHttpTransport.Builder>respectively.
Migration
Replace your customizer beans with the new generic interface, parameterized by the spec or builder type you need:
// Before
@Bean
public McpSyncClientCustomizer mySyncCustomizer() {
return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}
@Bean
public McpAsyncClientCustomizer myAsyncCustomizer() {
return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}
// After
@Bean
public McpClientCustomizer<McpClient.SyncSpec> mySyncCustomizer() {
return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}
@Bean
public McpClientCustomizer<McpClient.AsyncSpec> myAsyncCustomizer() {
return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}
For HttpClient transport customization (previously done via McpSyncHttpClientRequestCustomizer / McpAsyncHttpClientRequestCustomizer):
// Before
@Bean
public McpSyncHttpClientRequestCustomizer myRequestCustomizer() {
return requestBuilder -> requestBuilder.header("Authorization", "Bearer token");
}
// After
@Bean
public McpClientCustomizer<HttpClientSseClientTransport.Builder> mySseTransportCustomizer() {
return (name, builder) -> builder.httpRequestCustomizer(
req -> req.header("Authorization", "Bearer token")
);
}
Automated Migration with OpenRewrite
You can automate the import and type changes using the provided OpenRewrite recipe:
mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
-Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
-Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpClientCustomizer \
-Dmaven.compiler.failOnError=false
The recipe performs the following changes automatically:
-
Replaces the
McpAsyncClientCustomizerandMcpSyncClientCustomizerimports withMcpClientCustomizerand adds the requiredimport io.modelcontextprotocol.client.McpClient;. -
Rewrites
implements McpAsyncClientCustomizertoimplements McpClientCustomizer<McpClient.AsyncSpec>. -
Rewrites
implements McpSyncClientCustomizertoimplements McpClientCustomizer<McpClient.SyncSpec>. -
Rewrites all remaining usages (return types, variable declarations, parameter types).
McpSyncHttpClientRequestCustomizer and McpAsyncHttpClientRequestCustomizer beans are no longer applied by the transport auto-configurations. Their migration to McpClientCustomizer<TransportBuilder> requires a manual step — the target builder type depends on which transport you are configuring (SSE vs. Streamable HTTP).
|
To run all M3 migrations at once, use the umbrella recipe — see [run-all-m3-migrations].
MCP WebMvc Transport Headers Normalized to Lowercase
In WebMvcSseServerTransportProvider, WebMvcStatelessServerTransport, and WebMvcStreamableServerTransportProvider, the Map<String, List<String>> passed to securityValidator.validateHeaders(headers) now has all header names normalized to lowercase.
Previously, header names were passed with their original HTTP case (e.g. "Authorization", "Content-Type"). They are now always lowercase (e.g. "authorization", "content-type").
Impact
Custom ServerTransportSecurityValidator implementations that look up headers by their mixed-case names will silently fail to find them.
Migration
Update all header name lookups in your ServerTransportSecurityValidator to use lowercase keys:
// Before
public void validateHeaders(Map<String, List<String>> headers) {
List<String> authHeader = headers.get("Authorization");
// ...
}
// After
public void validateHeaders(Map<String, List<String>> headers) {
List<String> authHeader = headers.get("authorization");
// ...
}
Conversation History Removed from ToolContext
Conversation history is no longer automatically added to ToolContext. The TOOL_CALL_HISTORY constant and getToolCallHistory() method have been removed from the ToolContext class.
Impact
-
ToolContext.TOOL_CALL_HISTORYconstant no longer exists -
ToolContext.getToolCallHistory()method no longer exists -
Conversation history is no longer automatically populated in
ToolContext
Why This Change?
-
Memory Efficiency: Prevents unbounded memory growth in long conversations
-
Separation of Concerns: Tools should operate on their parameters, not manage conversation state
-
Architecture Alignment: Conversation context belongs at the advisor level, not in tool execution
Migration
If your application needs conversation history management, use ToolCallingAdvisor:
ChatClient chatClient = ChatClient.builder()
.defaultAdvisors(
ToolCallingAdvisor.builder()
.conversationHistoryEnabled(true) // Full history (default)
.build()
)
.build();
How ToolCallingAdvisor Works:
The ToolCallingAdvisor manages conversation history at the advisor level:
-
conversationHistoryEnabled=true (default): Full conversation history is maintained and sent to the LLM between tool call iterations, allowing the LLM to synthesize results with full context
-
conversationHistoryEnabled=false: Only the most recent tool response is sent to the LLM (useful when ChatMemory advisor manages history separately)
Key Point: The conversation history is used by the LLM to understand context and formulate responses, not by the tools themselves. Tools receive only their input parameters and any custom context you explicitly provide.
Custom Context in Tools:
ToolContext remains available for passing custom, application-specific data to tools:
ChatResponse response = chatClient.prompt()
.user("What's the weather in SF?")
.toolContext(Map.of("userId", "user123", "apiKey", "secret"))
.call()
.chatResponse();
Example Flow:
-
User asks: "What’s the weather in SF and LA?"
-
LLM requests tool calls:
getWeather(SF)andgetWeather(LA) -
Tools execute with only their parameters (no conversation history)
-
ToolCallingAdvisor collects tool results and conversation history
-
LLM receives conversation context from advisor and synthesizes: "The weather in SF is 72°F and in LA is 85°F"
The LLM sees the full conversation through the advisor chain, not through ToolContext.
The access level of model internal methods changed to private
-
All
internalCallandinternalStreammethods in model classes have been changed toprivate.
Migration
-
Replace all calls to
xxxModel.internalCallwithxxxModel.call. -
Replace all calls to
xxxModel.internalStreamwithxxxModel.stream.// Before ChatResponse response = model.internalCall(prompt, previousChatResponse); Flux<ChatResponse> responseFlux = model.internalStream(prompt, previousChatResponse); // After ChatResponse response = model.call(prompt); Flux<ChatResponse> responseFlux = model.stream(prompt);
OpenSearch Dependencies Upgraded
The OpenSearch vector store dependencies have been upgraded to newer versions:
-
OpenSearch Java Client:
2.23.0→3.6.0 -
OpenSearch Testcontainers:
2.0.1→4.1.0
Background
This upgrade was necessary for compatibility with Spring Boot 4.1.x, which uses HttpClient5 (org.apache.httpcomponents.client5:httpclient5) version 5.6. This version of HttpClient has a gzip content formatter breaking change that required the OpenSearch Java Client upgrade. See OpenSearch Java PR #1851 for details.
Impact
The OpenSearch Java Client 3.x introduces breaking API changes that affect custom code interacting directly with the native OpenSearch client.
Migration
If you’re using the OpenSearch vector store through Spring AI’s VectorStore interface, no action is required. The upgrade is transparent.
Testcontainers class renamed: OpensearchContainer → OpenSearchContainer (proper camelCase)
Spring AI’s internal implementation has been updated to handle these changes automatically.
AbstractFilterExpressionConverter: doSingleValue is now abstract
In AbstractFilterExpressionConverter (used by vector store filter expression converters), the method doSingleValue(Object value, StringBuilder context) has been changed from a concrete method to an abstract method. Custom vector store implementations that extend AbstractFilterExpressionConverter must now implement this method explicitly.
Impact
-
Any custom
FilterExpressionConverterthat extendsAbstractFilterExpressionConverterand did not overridedoSingleValue()will fail to compile. -
Implementations must convert a single filter value (String, Number, Boolean, Date, etc.) into the target format and append it to the provided
StringBuildercontext.
Migration
Implement doSingleValue(Object value, StringBuilder context) in your custom converter. You can use the provided static helper methods:
-
JSON-based filters (e.g. PostgreSQL JSONPath, Neo4j Cypher, Weaviate): use
emitJsonValue(Object value, StringBuilder context)to serialize values with proper quoting and escaping. -
Lucene-based filters (e.g. Elasticsearch, OpenSearch, GemFire): use
emitLuceneString(String value, StringBuilder context)for string values, and handle other types (numbers, booleans, dates) according to your store’s query syntax. -
Other formats: implement your own logic and append the result to
context.
The framework normalizes values (e.g. ISO date strings converted to Date) before invoking doSingleValue, so your implementation receives already-normalized values. The static helper normalizeDateString(Object) is available if you need the same normalization when building expressions outside of the standard flow.
|
MongoDB Chat Memory Message Ordering Fixed
The MongoChatMemoryRepository has been fixed to return messages in the order they were sent (oldest-to-newest), matching all other chat memory repository implementations. Previously, it incorrectly returned messages in reverse order (newest-to-oldest), which broke conversation flow for LLMs.
Impact
If your application was using MongoChatMemoryRepository and working around the incorrect ordering (e.g., by reversing messages after retrieval), you will need to remove that workaround.
Migration
Remove any code that reverses the message order after retrieving from MongoDB chat memory:
// BEFORE (with workaround for bug):
List<Message> messages = chatMemoryRepository.findByConversationId(conversationId);
Collections.reverse(messages); // Remove this workaround
// AFTER (correct ordering):
List<Message> messages = chatMemoryRepository.findByConversationId(conversationId);
// Messages are now correctly ordered chronologically
All chat memory repositories now consistently return messages in the order they were sent (oldest-to-newest), which is the expected format for LLM conversation history.
Development-time Services
-
Docker Compose and Testcontainers support for MongoDB Atlas is now provided natively by the Spring Boot MongoDB module. The migration should be transparent and not require any code change. Regarding dependencies, you don’t need to import
org.springframework.ai:spring-ai-spring-boot-testcontainersanymore. A dependency onorg.springframework.boot:spring-boot-testcontainersis sufficient.
Default Temperature Configuration Removed
Spring AI no longer provides default temperature values for chat model autoconfiguration properties. Previously, Spring AI set a default temperature of 0.7 for most chat models. This default has been removed to allow each AI provider’s native default temperature to be used.
Impact
If your application did not explicitly configure a temperature value and relied on Spring AI’s default of 0.7, you may notice different behavior after upgrading. The actual default will now be determined by each AI provider’s API, which may vary:
-
Some providers default to
1.0 -
Some providers default to
0.7 -
Some providers have model-specific defaults
Migration
If you want to maintain the previous behavior, explicitly set the temperature in your configuration:
# Example for OpenAI
spring.ai.openai.chat.temperature=0.7
# Example for Anthropic
spring.ai.anthropic.chat.temperature=0.7
# Example for Azure OpenAI
spring.ai.azure.openai.chat.temperature=0.7
Or programmatically when building requests:
ChatResponse response = chatModel.call(
new Prompt("Your prompt here",
OpenAiChatOptions.builder()
.temperature(0.7)
.build()));
Upgrading to 1.1.0-RC1
Breaking Changes
Text-to-Speech (TTS) API Migration
The OpenAI Text-to-Speech implementation has been migrated from provider-specific classes to shared interfaces. This enables writing portable code that works across multiple TTS providers (OpenAI, ElevenLabs, and future providers).
Removed Classes
The following deprecated classes have been removed from the org.springframework.ai.openai.audio.speech package:
-
SpeechModel→ UseTextToSpeechModel(fromorg.springframework.ai.audio.tts) -
StreamingSpeechModel→ UseStreamingTextToSpeechModel(fromorg.springframework.ai.audio.tts) -
SpeechPrompt→ UseTextToSpeechPrompt(fromorg.springframework.ai.audio.tts) -
SpeechResponse→ UseTextToSpeechResponse(fromorg.springframework.ai.audio.tts) -
SpeechMessage→ UseTextToSpeechMessage(fromorg.springframework.ai.audio.tts) -
Speech(inorg.springframework.ai.openai.audio.speech) → UseSpeech(fromorg.springframework.ai.audio.tts)
Additionally, the speed parameter type changed from Float to Double across all OpenAI TTS components for consistency with other TTS providers.
Migration Steps
-
Update Imports: Replace all imports from
org.springframework.ai.openai.audio.speech.withorg.springframework.ai.audio.tts. -
Update Type References: Replace all occurrences of the old class names with the new ones:
Find: SpeechModel Replace: TextToSpeechModel Find: StreamingSpeechModel Replace: StreamingTextToSpeechModel Find: SpeechPrompt Replace: TextToSpeechPrompt Find: SpeechResponse Replace: TextToSpeechResponse Find: SpeechMessage Replace: TextToSpeechMessage -
Update Speed Parameter: Change from
FloattoDouble:Find: .speed(1.0f) Replace: .speed(1.0) Find: Float speed Replace: Double speed -
Update Dependency Injection: If you inject
SpeechModel, update toTextToSpeechModel:// Before public MyService(SpeechModel speechModel) { ... } // After public MyService(TextToSpeechModel textToSpeechModel) { ... }
Benefits
-
Portability: Write code once, switch between OpenAI, ElevenLabs, or other TTS providers easily
-
Consistency: Same patterns as ChatModel and other Spring AI abstractions
-
Type Safety: Improved type hierarchy with proper interface implementations
-
Future-Proof: New TTS providers will automatically work with your existing code
Upgrading to 1.0.0-SNAPSHOT
Overview
The 1.0.0-SNAPSHOT version includes significant changes to artifact IDs, package names, and module structure. This section provides guidance specific to using the SNAPSHOT version.
Add Snapshot Repositories
To use the 1.0.0-SNAPSHOT version, you need to add the snapshot repositories to your build file. For detailed instructions, refer to the Snapshots - Add Snapshot Repositories section in the Getting Started guide.
Update Dependency Management
Update your Spring AI BOM version to 1.0.0-SNAPSHOT in your build configuration.
For detailed instructions on configuring dependency management, refer to the Dependency Management section in the Getting Started guide.
Artifact ID, Package, and Module Changes
The 1.0.0-SNAPSHOT includes changes to artifact IDs, package names, and module structure.
For details, refer to: - Common Artifact ID Changes - Common Package Changes - Common Module Structure
Upgrading to 1.0.0-RC1
You can automate the upgrade process to 1.0.0-RC1 using an OpenRewrite recipe. This recipe helps apply many of the necessary code changes for this version. Find the recipe and usage instructions at Arconia Spring AI Migrations.
Breaking Changes
Chat Client and Advisors
The main changes that impact end user code are:
-
In
VectorStoreChatMemoryAdvisor:-
The constant
CHAT_MEMORY_RETRIEVE_SIZE_KEYhas been renamed toTOP_K. -
The constant
DEFAULT_CHAT_MEMORY_RESPONSE_SIZE(value: 100) has been renamed toDEFAULT_TOP_Kwith a new default value of 20.
-
-
The constant
CHAT_MEMORY_CONVERSATION_ID_KEYhas been renamed toCONVERSATION_IDand moved fromAbstractChatMemoryAdvisorto theChatMemoryinterface. Update your imports to useorg.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID.
Self-contained Templates in Advisors
The built-in advisors that perform prompt augmentation have been updated to use self-contained templates. The goal is for each advisor to be able to perform templating operations without affecting nor being affected by templating and prompt decisions in other advisors.
If you were providing custom templates for the following advisors, you’ll need to update them to ensure all expected placeholders are included.
-
The
QuestionAnswerAdvisorexpects a template with the following placeholders (see more details):-
a
queryplaceholder to receive the user question. -
a
question_answer_contextplaceholder to receive the retrieved context.
-
-
The
VectorStoreChatMemoryAdvisorexpects a template with the following placeholders (see more details):-
an
instructionsplaceholder to receive the original system message. -
a
long_term_memoryplaceholder to receive the retrieved conversation memory.
-
Observability
-
Refactored content observation to use logging instead of tracing (ca843e8)
-
Replaced content observation filters with logging handlers
-
Renamed configuration properties to better reflect their purpose:
-
include-prompt→log-prompt -
include-completion→log-completion -
include-query-response→log-query-response
-
-
Added
TracingAwareLoggingObservationHandlerfor trace-aware logging -
Replaced
micrometer-tracing-bridge-otelwithmicrometer-tracing -
Removed event-based tracing in favor of direct logging
-
Removed direct dependency on the OTel SDK
-
Renamed
includePrompttologPromptin observation properties (inChatClientBuilderProperties,ChatObservationProperties, andImageObservationProperties)
-
Chat Memory Repository Module and Autoconfiguration Renaming
We’ve standardized the naming pattern for chat memory components by adding the repository suffix throughout the codebase. This change affects Cassandra, JDBC, and Neo4j implementations, impacting artifact IDs, Java package names, and class names for clarity.
Artifact IDs
All memory-related artifacts now follow a consistent pattern:
-
spring-ai-model-chat-memory-→spring-ai-model-chat-memory-repository- -
spring-ai-autoconfigure-model-chat-memory-→spring-ai-autoconfigure-model-chat-memory-repository- -
spring-ai-starter-model-chat-memory-→spring-ai-starter-model-chat-memory-repository-
Java Packages
-
Package paths now include
.repository.segment -
Example:
org.springframework.ai.chat.memory.jdbc→org.springframework.ai.chat.memory.repository.jdbc
Configuration Classes
-
Main autoconfiguration classes now use the
Repositorysuffix -
Example:
JdbcChatMemoryAutoConfiguration→JdbcChatMemoryRepositoryAutoConfiguration
Properties
-
Configuration properties renamed from
spring.ai.chat.memory.<storage>…tospring.ai.chat.memory.repository.<storage>…
Migration Required: - Update your Maven/Gradle dependencies to use the new artifact IDs. - Update any imports, class references, or configuration that used the old package or class names.
Message Aggregator Refactoring
Changes
-
MessageAggregatorclass has been moved fromorg.springframework.ai.chat.modelpackage in thespring-ai-client-chatmodule to thespring-ai-modelmodule (same package name) -
The
aggregateChatClientResponsemethod has been removed fromMessageAggregatorand moved to a new classChatClientMessageAggregatorin theorg.springframework.ai.chat.clientpackage
Migration Guide
If you were directly using the aggregateChatClientResponse method from MessageAggregator, you need to use the new ChatClientMessageAggregator class instead:
// Before
new MessageAggregator().aggregateChatClientResponse(chatClientResponses, aggregationHandler);
// After
new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, aggregationHandler);
Don’t forget to add the appropriate import:
import org.springframework.ai.chat.client.ChatClientMessageAggregator;
Watson
The Watson AI model was removed as it was based on the older text generation that is considered outdated as there is a new chat generation model available. Hopefully Watson will reappear in a future version of Spring AI
MoonShot and QianFan
Moonshot and Qianfan have been removed since they are not accessible from outside China. These have been moved to the Spring AI Community repository.
Removed Vector Store
-
Removed HanaDB vector store autoconfiguration (f3b4624)
Memory Management
-
Removed CassandraChatMemory implementation (11e3c8f)
-
Simplified chat memory advisor hierarchy and removed deprecated API (848a3fd)
-
Removed deprecations in JdbcChatMemory (356a68f)
-
Refactored chat memory repository artifacts for clarity (2d517ee)
-
Refactored chat memory repository autoconfigurations and Spring Boot starters for clarity (f6dba1b)
Message and Template APIs
-
Removed deprecated UserMessage constructors (06edee4)
-
Removed deprecated PromptTemplate constructors (722c77e)
-
Removed deprecated methods from Media (228ef10)
-
Refactored StTemplateRenderer: renamed supportStFunctions to validateStFunctions (0e15197)
-
Removed left over TemplateRender interface after moving it (52675d8)
Dependencies
-
Removed unused json-path dependency in spring-ai-openai (9de13d1)
Behavior Changes
Azure OpenAI
-
Added Entra ID identity management for Azure OpenAI with clean autoconfiguration (3dc86d3)
Upgrading to 1.0.0-M8
You can automate the upgrade process to 1.0.0-M8 using an OpenRewrite recipe. This recipe helps apply many of the necessary code changes for this version. Find the recipe and usage instructions at Arconia Spring AI Migrations.
Breaking Changes
When upgrading from Spring AI 1.0 M7 to 1.0 M8, users who previously registered tool callbacks are encountering breaking changes that cause tool calling functionality to silently fail. This is specifically impacting code that used the deprecated tools() method.
Example
Here’s an example of code that worked in M7 but no longer functions as expected in M8:
// This worked in M7 but silently fails in M8
ChatClient chatClient = new OpenAiChatClient(api)
.tools(List.of(
new Tool("get_current_weather", "Get the current weather in a given location",
new ToolSpecification.ToolParameter("location", "The city and state, e.g. San Francisco, CA", true))
))
.toolCallbacks(List.of(
new ToolCallback("get_current_weather", (toolName, params) -> {
// Weather retrieval logic
return Map.of("temperature", 72, "unit", "fahrenheit", "description", "Sunny");
})
));
Solution
The solution is to use the toolSpecifications() method instead of the deprecated tools() method:
// This works in M8
ChatClient chatClient = new OpenAiChatClient(api)
.toolSpecifications(List.of(
new Tool("get_current_weather", "Get the current weather in a given location",
new ToolSpecification.ToolParameter("location", "The city and state, e.g. San Francisco, CA", true))
))
.toolCallbacks(List.of(
new ToolCallback("get_current_weather", (toolName, params) -> {
// Weather retrieval logic
return Map.of("temperature", 72, "unit", "fahrenheit", "description", "Sunny");
})
));
Removed Implementations and APIs
Memory Management
-
Removed CassandraChatMemory implementation (11e3c8f)
-
Simplified chat memory advisor hierarchy and removed deprecated API (848a3fd)
-
Removed deprecations in JdbcChatMemory (356a68f)
-
Refactored chat memory repository artifacts for clarity (2d517ee)
-
Refactored chat memory repository autoconfigurations and Spring Boot starters for clarity (f6dba1b)
Message and Template APIs
-
Removed deprecated UserMessage constructors (06edee4)
-
Removed deprecated PromptTemplate constructors (722c77e)
-
Removed deprecated methods from Media (228ef10)
-
Refactored StTemplateRenderer: renamed supportStFunctions to validateStFunctions (0e15197)
-
Removed left over TemplateRender interface after moving it (52675d8)
Dependencies
-
Removed unused json-path dependency in spring-ai-openai (9de13d1)
Behavior Changes
Observability
-
Refactored content observation to use logging instead of tracing (ca843e8)
-
Replaced content observation filters with logging handlers
-
Renamed configuration properties to better reflect their purpose:
-
include-prompt→log-prompt -
include-completion→log-completion -
include-query-response→log-query-response
-
-
Added
TracingAwareLoggingObservationHandlerfor trace-aware logging -
Replaced
micrometer-tracing-bridge-otelwithmicrometer-tracing -
Removed event-based tracing in favor of direct logging
-
Removed direct dependency on the OTel SDK
-
Renamed
includePrompttologPromptin observation properties (inChatClientBuilderProperties,ChatObservationProperties, andImageObservationProperties)
-
Azure OpenAI
-
Added Entra ID identity management for Azure OpenAI with clean autoconfiguration (3dc86d3)
Upgrading to 1.0.0-M7
Overview of Changes
Spring AI 1.0.0-M7 is the last milestone release before the RC1 and GA releases. It introduces several important changes to artifact IDs, package names, and module structure that will be maintained in the final release.
Artifact ID, Package, and Module Changes
The 1.0.0-M7 includes the same structural changes as 1.0.0-SNAPSHOT.
For details, refer to: - Common Artifact ID Changes - Common Package Changes - Common Module Structure
MCP Java SDK Upgrade to 0.9.0
Spring AI 1.0.0-M7 now uses MCP Java SDK version 0.9.0, which includes significant changes from previous versions. If you’re using MCP in your applications, you’ll need to update your code to accommodate these changes.
Key changes include:
Interface Renaming
-
ClientMcpTransport→McpClientTransport -
ServerMcpTransport→McpServerTransport -
DefaultMcpSession→McpClientSessionorMcpServerSession -
All
*Registrationclasses →*Specificationclasses
Server Creation Changes
-
Use
McpServerTransportProviderinstead ofServerMcpTransport
// Before
ServerMcpTransport transport = new WebFluxSseServerTransport(objectMapper, "/mcp/message");
var server = McpServer.sync(transport)
.serverInfo("my-server", "1.0.0")
.build();
// After
McpServerTransportProvider transportProvider = new WebFluxSseServerTransportProvider(objectMapper, "/mcp/message");
var server = McpServer.sync(transportProvider)
.serverInfo("my-server", "1.0.0")
.build();
Handler Signature Changes
All handlers now receive an exchange parameter as their first argument:
// Before
.tool(calculatorTool, args -> new CallToolResult("Result: " + calculate(args)))
// After
.tool(calculatorTool, (exchange, args) -> new CallToolResult("Result: " + calculate(args)))
Client Interaction via Exchange
Methods previously available on the server are now accessed through the exchange object:
// Before
ClientCapabilities capabilities = server.getClientCapabilities();
CreateMessageResult result = server.createMessage(new CreateMessageRequest(...));
// After
ClientCapabilities capabilities = exchange.getClientCapabilities();
CreateMessageResult result = exchange.createMessage(new CreateMessageRequest(...));
Roots Change Handlers
// Before
.rootsChangeConsumers(List.of(
roots -> System.out.println("Roots changed: " + roots)
))
// After
.rootsChangeHandlers(List.of(
(exchange, roots) -> System.out.println("Roots changed: " + roots)
))
For a complete guide to migrating MCP code, refer to the MCP Migration Guide.
Enabling/Disabling Model Auto-Configuration
The previous configuration properties for enabling/disabling model auto-configuration have been removed:
-
spring.ai.<provider>.chat.enabled -
spring.ai.<provider>.embedding.enabled -
spring.ai.<provider>.image.enabled -
spring.ai.<provider>.moderation.enabled
By default, if a model provider (e.g., OpenAI, Ollama) is found on the classpath, its corresponding auto-configuration for relevant model types (chat, embedding, etc.) is enabled. If multiple providers for the same model type are present (e.g., both spring-ai-openai-spring-boot-starter and spring-ai-ollama-spring-boot-starter), you can use the following properties to select which provider’s auto-configuration should be active, effectively disabling the others for that specific model type.
To disable auto-configuration for a specific model type entirely, even if only one provider is present, set the corresponding property to a value that does not match any provider on the classpath (e.g., none or disabled).
You can refer to the SpringAIModels enumeration for a list of well-known provider values.
-
spring.ai.model.audio.speech=<model-provider|none> -
spring.ai.model.audio.transcription=<model-provider|none> -
spring.ai.model.chat=<model-provider|none> -
spring.ai.model.embedding=<model-provider|none> -
spring.ai.model.embedding.multimodal=<model-provider|none> -
spring.ai.model.embedding.text=<model-provider|none> -
spring.ai.model.image=<model-provider|none> -
spring.ai.model.moderation=<model-provider|none>
Automating upgrading using AI
You can automate the upgrade process to 1.0.0-M7 using the Claude Code CLI tool with a provided prompt:
-
Download the Claude Code CLI tool
-
Copy the prompt from the update-to-m7.txt file
-
Paste the prompt into the Claude Code CLI
-
The AI will analyze your project and make the necessary changes
| The automated upgrade prompt currently handles artifact ID changes, package relocations, and module structure changes, but does not yet include automatic changes for upgrading to MCP 0.9.0. If you’re using MCP, you’ll need to manually update your code following the guidance in the MCP Java SDK Upgrade section. |
Common Changes Across Versions
Artifact ID Changes
The naming pattern for Spring AI starter artifacts has changed. You’ll need to update your dependencies according to the following patterns:
-
Model starters:
spring-ai-{model}-spring-boot-starter→spring-ai-starter-model-{model} -
Vector Store starters:
spring-ai-{store}-store-spring-boot-starter→spring-ai-starter-vector-store-{store} -
MCP starters:
spring-ai-mcp-{type}-spring-boot-starter→spring-ai-starter-mcp-{type}
Examples
-
Maven
-
Gradle
<!-- BEFORE -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- AFTER -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
// BEFORE
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
implementation 'org.springframework.ai:spring-ai-redis-store-spring-boot-starter'
// AFTER
implementation 'org.springframework.ai:spring-ai-starter-model-openai'
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis'
Changes to Spring AI Autoconfiguration Artifacts
The Spring AI autoconfiguration has changed from a single monolithic artifact to individual autoconfiguration artifacts per model, vector store, and other components. This change was made to minimize the impact of different versions of dependent libraries conflicting, such as Google Protocol Buffers, Google RPC, and others. By separating autoconfiguration into component-specific artifacts, you can avoid pulling in unnecessary dependencies and reduce the risk of version conflicts in your application.
The original monolithic artifact is no longer available:
<!-- NO LONGER AVAILABLE -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
Instead, each component now has its own autoconfiguration artifact following these patterns:
-
Model autoconfiguration:
spring-ai-autoconfigure-model-{model} -
Vector Store autoconfiguration:
spring-ai-autoconfigure-vector-store-{store} -
MCP autoconfiguration:
spring-ai-autoconfigure-mcp-{type}
Examples of New Autoconfiguration Artifacts
-
Models
-
Vector Stores
-
MCP
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-anthropic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-pgvector</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-vector-store-chroma</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-mcp-server</artifactId>
</dependency>
| In most cases, you won’t need to explicitly add these autoconfiguration dependencies. They are included transitively when using the corresponding starter dependencies. |
Package Name Changes
Your IDE should assist with refactoring to the new package locations.
-
KeywordMetadataEnricherandSummaryMetadataEnricherhave moved fromorg.springframework.ai.transformertoorg.springframework.ai.chat.transformer. -
Content,MediaContent, andMediahave moved fromorg.springframework.ai.modeltoorg.springframework.ai.content.
Module Structure
The project has undergone significant changes to its module and artifact structure. Previously, spring-ai-core contained all central interfaces, but this has now been split into specialized domain modules to reduce unnecessary dependencies in your applications.
spring-ai-commons
Base module with no dependencies on other Spring AI modules. Contains:
- Core domain models (Document, TextSplitter)
- JSON utilities and resource handling
- Structured logging and observability support
spring-ai-model
Provides AI capability abstractions:
- Interfaces like ChatModel, EmbeddingModel, and ImageModel
- Message types and prompt templates
- Function-calling framework (ToolDefinition, ToolCallback)
- Content filtering and observation support
spring-ai-vector-store
Unified vector database abstraction:
- VectorStore interface for similarity search
- Advanced filtering with SQL-like expressions
- SimpleVectorStore for in-memory usage
- Batching support for embeddings
spring-ai-client-chat
High-level conversational AI APIs:
- ChatClient interface
- Conversation persistence via ChatMemory
- Response conversion with OutputConverter
- Advisor-based interception
- Synchronous and reactive streaming support
spring-ai-vector-store-advisor
Bridges chat with vector stores for RAG:
- QuestionAnswerAdvisor: injects context into prompts
- VectorStoreChatMemoryAdvisor: stores/retrieves conversation history
Dependency Structure
The dependency hierarchy can be summarized as:
-
spring-ai-commons(foundation) -
spring-ai-model(depends on commons) -
spring-ai-vector-storeandspring-ai-client-chat(both depend on model) -
spring-ai-vector-store-advisorandspring-ai-rag(depend on both client-chat and vector-store) -
spring-ai-model-chat-memory-*modules (depend on client-chat)
ToolContext Changes
The ToolContext class has been enhanced to support both explicit and implicit tool resolution. Tools can now be:
-
Explicitly Included: Tools that are explicitly requested in the prompt and included in the call to the model.
-
Implicitly Available: Tools that are made available for runtime dynamic resolution, but never included in any call to the model unless explicitly requested.
Starting with 1.0.0-M7, tools are only included in the call to the model if they are explicitly requested in the prompt or explicitly included in the call.
Additionally, the ToolContext class has now been marked as final and cannot be extended anymore. It was never supposed to be subclassed. You can add all the contextual data you need when instantiating a ToolContext, in the form of a Map<String, Object>. For more information, check the [documentation](docs.spring.io/spring-ai/reference/api/tools.html#_tool_context).
Upgrading to 1.0.0-M6
Changes to Usage Interface and DefaultUsage Implementation
The Usage interface and its default implementation DefaultUsage have undergone the following changes:
-
Method Rename:
-
getGenerationTokens()is nowgetCompletionTokens()
-
-
Type Changes:
-
All token count fields in
DefaultUsagechanged fromLongtoInteger:-
promptTokens -
completionTokens(formerlygenerationTokens) -
totalTokens
-
-
Required Actions
-
Replace all calls to
getGenerationTokens()withgetCompletionTokens() -
Update
DefaultUsageconstructor calls:
// Old (M5) new DefaultUsage(Long promptTokens, Long generationTokens, Long totalTokens) // New (M6) new DefaultUsage(Integer promptTokens, Integer completionTokens, Integer totalTokens)
| For more information on handling Usage, refer here |
JSON Ser/Deser changes
While M6 maintains backward compatibility for JSON deserialization of the generationTokens field, this field will be removed in M7. Any persisted JSON documents using the old field name should be updated to use completionTokens.
Example of the new JSON format:
{
"promptTokens": 100,
"completionTokens": 50,
"totalTokens": 150
}
Changes to usage of FunctionCallingOptions for tool calling
Each ChatModel instance, at construction time, accepts an optional ChatOptions or FunctionCallingOptions instance
that can be used to configure default tools used for calling the model.
Before 1.0.0-M6:
-
any tool passed via the
functions()method of the defaultFunctionCallingOptionsinstance was included in each call to the model from thatChatModelinstance, possibly overwritten by runtime options. -
any tool passed via the
functionCallbacks()method of the defaultFunctionCallingOptionsinstance was only made available for runtime dynamic resolution (see Tool Resolution), but never included in any call to the model unless explicitly requested.
Starting 1.0.0-M6:
-
any tool passed via the
functions()method or thefunctionCallbacks()of the defaultFunctionCallingOptionsinstance is now handled in the same way: it is included in each call to the model from thatChatModelinstance, possibly overwritten by runtime options. With that, there is consistency in the way tools are included in calls to the model and prevents any confusion due to a difference in behavior betweenfunctionCallbacks()and all the other options.
If you want to make a tool available for runtime dynamic resolution and include it in a chat request to the model only when explicitly requested, you can use one of the strategies described in Tool Resolution.
| 1.0.0-M6 introduced new APIs for handling tool calling. Backward compatibility is maintained for the old APIs across all scenarios, except the one described above. The old APIs are still available, but they are deprecated and will be removed in 1.0.0-M7. |
Removal of deprecated Amazon Bedrock chat models
Starting 1.0.0-M6, Spring AI transitioned to using Amazon Bedrock’s Converse API for all Chat conversation implementations in Spring AI. All the Amazon Bedrock Chat models are removed except the Embedding models for Cohere and Titan.
| Refer to Bedrock Converse documentation for using the chat models. |
Changes to use Spring Boot 3.4.2 for dependency management
Spring AI updates to use Spring Boot 3.4.2 for the dependency management. You can refer here for the dependencies managed by Spring Boot 3.4.2
Required Actions
-
If you are upgrading to Spring Boot 3.4.2, please make sure to refer to this documentation for the changes required to configure the REST Client. Notably, if you don’t have an HTTP client library on the classpath, this will likely result in the use of
JdkClientHttpRequestFactorywhereSimpleClientHttpRequestFactorywould have been used previously. To switch to useSimpleClientHttpRequestFactory, you need to setspring.http.client.factory=simple. -
If you are using a different version of Spring Boot (say Spring Boot 3.3.x) and need a specific version of a dependency, you can override it in your build configuration.
Vector Store API changes
In version 1.0.0-M6, the delete method in the VectorStore interface has been modified to be a void operation instead of returning an Optional<Boolean>.
If your code previously checked the return value of the delete operation, you’ll need to remove this check.
The operation now throws an exception if the deletion fails, providing more direct error handling.
Upgrading to 1.0.0.M5
-
Vector Builders have been refactored for consistency.
-
Current VectorStore implementation constructors have been deprecated, use the builder pattern.
-
VectorStore implementation packages have been moved into unique package names, avoiding conflicts across artifact. For example
org.springframework.ai.vectorstoretoorg.springframework.ai.pgvector.vectorstore.
Upgrading to 1.0.0.RC3
-
The type of the portable chat options (
frequencyPenalty,presencePenalty,temperature,topP) has been changed fromFloattoDouble.
Upgrading to 1.0.0.M2
-
The configuration prefix for the Chroma Vector Store has been changes from
spring.ai.vectorstore.chroma.storetospring.ai.vectorstore.chromain order to align with the naming conventions of other vector stores. -
The default value of the
initialize-schemaproperty on vector stores capable of initializing a schema is now set tofalse. This implies that the applications now need to explicitly opt-in for schema initialization on supported vector stores, if the schema is expected to be created at application startup. Not all vector stores support this property. See the corresponding vector store documentation for more details. The following are the vector stores that currently don’t support theinitialize-schemaproperty.-
Pinecone
-
Weaviate
-
-
In Bedrock Jurassic 2, the chat options
countPenalty,frequencyPenalty, andpresencePenaltyhave been renamed tocountPenaltyOptions,frequencyPenaltyOptions, andpresencePenaltyOptions. Furthermore, the type of the chat optionstopSequenceshave been changed fromString[]toList<String>. -
In Azure OpenAI, the type of the chat options
frequencyPenaltyandpresencePenaltyhas been changed fromDoubletoFloat, consistently with all the other implementations.
Upgrading to 1.0.0.M1
On our march to release 1.0.0 M1 we have made several breaking changes. Apologies, it is for the best!
ChatClient changes
A major change was made that took the 'old' ChatClient and moved the functionality into ChatModel. The 'new' ChatClient now takes an instance of ChatModel. This was done to support a fluent API for creating and executing prompts in a style similar to other client classes in the Spring ecosystem, such as RestClient, WebClient, and JdbcClient. Refer to the [JavaDoc](docs.spring.io/spring-ai/docs/api) for more information on the Fluent API, proper reference documentation is coming shortly.
We renamed the 'old' ModelClient to Model and renamed implementing classes, for example ImageClient was renamed to ImageModel. The Model implementation represents the portability layer that converts between the Spring AI API and the underlying AI Model API.
A new package model that contains interfaces and base classes to support creating AI Model Clients for any input/output data type combination. At the moment, the chat and image model packages implement this. We will be updating the embedding package to this new model soon.
A new "portable options" design pattern. We wanted to provide as much portability in the ModelCall as possible across different chat based AI Models. There is a common set of generation options and then those that are specific to a model provider. A sort of "duck typing" approach is used. ModelOptions in the model package is a marker interface indicating implementations of this class will provide the options for a model. See ImageOptions, a subinterface that defines portable options across all text→image ImageModel implementations. Then StabilityAiImageOptions and OpenAiImageOptions provide the options specific to each model provider. All options classes are created via a fluent API builder, all can be passed into the portable ImageModel API. These option data types are used in autoconfiguration/configuration properties for the ImageModel implementations.
Artifact name changes
Renamed POM artifact names: - spring-ai-qdrant → spring-ai-qdrant-store - spring-ai-cassandra → spring-ai-cassandra-store - spring-ai-pinecone → spring-ai-pinecone-store - spring-ai-redis → spring-ai-redis-store - spring-ai-qdrant → spring-ai-qdrant-store - spring-ai-gemfire → spring-ai-gemfire-store - spring-ai-azure-vector-store-spring-boot-starter → spring-ai-azure-store-spring-boot-starter - spring-ai-redis-spring-boot-starter → spring-ai-starter-vector-store-redis
Upgrading to 0.8.1
Former spring-ai-vertex-ai has been renamed to spring-ai-vertex-ai-palm2 and spring-ai-vertex-ai-spring-boot-starter has been renamed to spring-ai-vertex-ai-palm2-spring-boot-starter.
So, you need to change the dependency from
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vertex-ai</artifactId>
</dependency>
To
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vertex-ai-palm2</artifactId>
</dependency>
and the related Boot starter for the Palm2 model has changed from
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vertex-ai-spring-boot-starter</artifactId>
</dependency>
to
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vertex-ai-palm2-spring-boot-starter</artifactId>
</dependency>
-
Renamed Classes (01.03.2024)
-
VertexAiApi → VertexAiPalm2Api
-
VertexAiClientChat → VertexAiPalm2ChatClient
-
VertexAiEmbeddingClient → VertexAiPalm2EmbeddingClient
-
VertexAiChatOptions → VertexAiPalm2ChatOptions
-
Upgrading to 0.8.0
January 24, 2024 Update
-
Moving the
promptandmessagesandmetadatapackages to subpackages oforg.springframework.ai.chat -
New functionality is text to image clients. Classes are
OpenAiImageModelandStabilityAiImageModel. See the integration tests for usage, docs are coming soon. -
A new package
modelthat contains interfaces and base classes to support creating AI Model Clients for any input/output data type combination. At the moment, the chat and image model packages implement this. We will be updating the embedding package to this new model soon. -
A new "portable options" design pattern. We wanted to provide as much portability in the
ModelCallas possible across different chat based AI Models. There is a common set of generation options and then those that are specific to a model provider. A sort of "duck typing" approach is used.ModelOptionsin the model package is a marker interface indicating implementations of this class will provide the options for a model. SeeImageOptions, a subinterface that defines portable options across all text→imageImageModelimplementations. ThenStabilityAiImageOptionsandOpenAiImageOptionsprovide the options specific to each model provider. All options classes are created via a fluent API builder, all can be passed into the portableImageModelAPI. These option data types are used in autoconfiguration/configuration properties for theImageModelimplementations.
January 13, 2024 Update
The following OpenAi Autoconfiguration chat properties have changed
-
from
spring.ai.openai.modeltospring.ai.openai.chat.model. -
from
spring.ai.openai.temperaturetospring.ai.openai.chat.temperature.
Find updated documentation about the OpenAi properties: docs.spring.io/spring-ai/reference/api/chat/openai-chat.html
December 27, 2023 Update
Merge SimplePersistentVectorStore and InMemoryVectorStore into SimpleVectorStore * Replace InMemoryVectorStore with SimpleVectorStore
December 20, 2023 Update
Refactor the Ollama client and related classes and package names
-
Replace the org.springframework.ai.ollama.client.OllamaClient by org.springframework.ai.ollama.OllamaModelCall.
-
The OllamaChatClient method signatures have changed.
-
Rename the org.springframework.ai.autoconfigure.ollama.OllamaProperties into org.springframework.ai.model.ollama.autoconfigure.OllamaChatProperties and change the suffix to:
spring.ai.ollama.chat. Some of the properties have changed as well.
December 19, 2023 Update
Renaming of AiClient and related classes and package names
-
Rename AiClient to ChatClient
-
Rename AiResponse to ChatResponse
-
Rename AiStreamClient to StreamingChatClient
-
Rename package org.sf.ai.client to org.sf.ai.chat
Rename artifact ID of
-
transformers-embeddingtospring-ai-transformers
Moved Maven modules from top-level directory and embedding-clients subdirectory to all be under a single models directory.
December 1, 2023
We are transitioning the project’s Group ID:
-
FROM:
org.springframework.experimental.ai -
TO:
org.springframework.ai
Artifacts will still be hosted in the snapshot repository as shown below.
The main branch will move to the version 0.8.0-SNAPSHOT.
It will be unstable for a week or two.
Please use the 0.7.1-SNAPSHOT if you don’t want to be on the bleeding edge.
You can access 0.7.1-SNAPSHOT artifacts as before and still access 0.7.1-SNAPSHOT Documentation.
0.7.1-SNAPSHOT Dependencies
-
Azure OpenAI
<dependency> <groupId>org.springframework.experimental.ai</groupId> <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId> <version>0.7.1-SNAPSHOT</version> </dependency> -
OpenAI
<dependency> <groupId>org.springframework.experimental.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>0.7.1-SNAPSHOT</version> </dependency>