Companion code for the ankurm.com guide. Verified on Spring AI 2.0.0, Spring Boot 4.1.0,
JDK 25.0.3. run.txt is unedited mvn test output: 5 tests, 0 failures.
Runs with NO API key, no network and no provider account. A canned ChatModel returns fixed
responses; ChatClient, advisors and chat memory all sit above the model, so everything that
changed is still exercised faithfully. TestChatModel records each Prompt it receives, which
lets the tests assert what Spring AI SENT rather than what a model replied.
docs/01 complete breaking-change reference: platform (Boot 4 mandatory, Jackson 2 to 3),
artifact renames (spring-ai-advisors-vector-store -> spring-ai-vector-store-advisor,
spring-ai-core split, OCI and Minimax removals, MCP transports moved into Spring AI),
options builders replacing setters, the dropped .options property prefix, tool
calling (internalToolExecutionEnabled and toolNames removed outright, not renamed),
chat memory, structured output schema changes, and a migration order that works.
Tested here
- ChatMemory.DEFAULT_CONVERSATION_ID is gone; CONVERSATION_ID survives as the metadata KEY.
The two look interchangeable and are opposites - swapping them to clear a compile error
silently restores the shared-conversation bug the removal was meant to prevent.
- PromptChatMemoryAdvisor is gone; MessageChatMemoryAdvisor replaces it, and the difference
is not cosmetic (system-prompt text vs real Message objects, so token counts change).
- Conversation id is now a per-request advisor param, provable by a test showing two
conversations cannot see each other. In 1.x, omitting it put every user in one bucket.
119 lines
13 KiB
Plaintext
119 lines
13 KiB
Plaintext
mvn.cmd : WARNING: A restricted method in java.lang.System has been called
|
|
At line:5 char:1
|
|
+ & $mvn -B test *> run.txt
|
|
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+ CategoryInfo : NotSpecified: (WARNING: A rest...has been called:String) [], RemoteException
|
|
+ FullyQualifiedErrorId : NativeCommandError
|
|
|
|
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module
|
|
(file:/C:/Users/Ankur/ankurm-blog-tools/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
|
|
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
|
|
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
|
|
|
|
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
|
|
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by
|
|
com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
|
|
(file:/C:/Users/Ankur/ankurm-blog-tools/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
|
|
WARNING: Please consider reporting this to the maintainers of class
|
|
com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
|
|
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
|
|
[INFO] Scanning for projects...
|
|
[INFO]
|
|
[INFO] ----------------< com.ankurm.ai:spring-ai-2-migration >-----------------
|
|
[INFO] Building Spring AI 1.x to 2.0 migration 1.0.0
|
|
[INFO] from pom.xml
|
|
[INFO] --------------------------------[ jar ]---------------------------------
|
|
[INFO]
|
|
[INFO] --- resources:3.5.0:resources (default-resources) @ spring-ai-2-migration ---
|
|
[INFO] skip non existing resourceDirectory C:\Users\Ankur\ankurm-blog-tools\projects\spring-ai-2-migration\src\main\resources
|
|
[INFO] skip non existing resourceDirectory C:\Users\Ankur\ankurm-blog-tools\projects\spring-ai-2-migration\src\main\resources
|
|
[INFO]
|
|
[INFO] --- compiler:3.15.0:compile (default-compile) @ spring-ai-2-migration ---
|
|
[INFO] Recompiling the module because of changed source code.
|
|
[INFO] Compiling 1 source file with javac [debug parameters release 25] to target\classes
|
|
[INFO]
|
|
[INFO] --- resources:3.5.0:testResources (default-testResources) @ spring-ai-2-migration ---
|
|
[INFO] skip non existing resourceDirectory C:\Users\Ankur\ankurm-blog-tools\projects\spring-ai-2-migration\src\test\resources
|
|
[INFO]
|
|
[INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ spring-ai-2-migration ---
|
|
[INFO] Recompiling the module because of changed dependency.
|
|
[INFO] Compiling 1 source file with javac [debug parameters release 25] to target\test-classes
|
|
[INFO]
|
|
[INFO] --- surefire:3.5.6:test (default-test) @ spring-ai-2-migration ---
|
|
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
|
|
[INFO]
|
|
[INFO] -------------------------------------------------------
|
|
[INFO] T E S T S
|
|
[INFO] -------------------------------------------------------
|
|
[INFO] Running com.ankurm.ai.ChatMemoryMigrationTest
|
|
|
|
==============================================================================
|
|
4. Proving isolation -- the bug the 1.x default was hiding
|
|
==============================================================================
|
|
bob's prompt: USER what did you hear?
|
|
|
|
>> In 1.x, omitting the id on both calls put alice and bob in the same
|
|
>> 'default' conversation and this assertion would have failed --
|
|
>> silently, in production, as a cross-user data leak.
|
|
|
|
==============================================================================
|
|
1. ChatMemory.DEFAULT_CONVERSATION_ID has been removed
|
|
==============================================================================
|
|
public constants on ChatMemory : [CONVERSATION_ID]
|
|
|
|
Note the survivor is CONVERSATION_ID, the metadata key you use to PASS an id.
|
|
The removed one was DEFAULT_CONVERSATION_ID, the value 'default' used when you
|
|
passed nothing. Same prefix, opposite meaning -- easy to 'fix' a compile error
|
|
by swapping one for the other and reintroducing the shared-bucket bug.
|
|
|
|
==============================================================================
|
|
2. PromptChatMemoryAdvisor has been removed
|
|
==============================================================================
|
|
PromptChatMemoryAdvisor : ClassNotFoundException (as expected in 2.0)
|
|
MessageChatMemoryAdvisor: org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor
|
|
|
|
Replacement is MessageChatMemoryAdvisor. The difference is not cosmetic: the
|
|
removed one injected history into the SYSTEM PROMPT as text, the replacement
|
|
adds it as real Message objects. Providers treat those differently, so expect
|
|
your prompts -- and your token counts -- to change on migration.
|
|
|
|
==============================================================================
|
|
5. Direct ChatMemory use, for the storage-layer migration
|
|
==============================================================================
|
|
stored: first
|
|
stored: second
|
|
after clear: 0 messages
|
|
|
|
>> If you use a JDBC/Cassandra/Mongo/Neo4j ChatMemoryRepository, note a
|
|
>> separate 2.0 change: retrieved messages now carry a creation
|
|
>> timestamp in metadata, so a message read back is NOT equals() to an
|
|
>> identical one built in code. Any test or cache keyed on Message
|
|
>> equality, or storing Messages in a Set, will change behaviour.
|
|
|
|
==============================================================================
|
|
3. The conversation ID is now required, per request
|
|
==============================================================================
|
|
turn 1 -- conversation 'user-42'
|
|
turn 2 -- same conversation
|
|
messages the model received on turn 2 : 3
|
|
USER my name is Ankur
|
|
ASSISTANT hello back
|
|
USER what is my name?
|
|
|
|
>> The id travels as an advisor PARAM per request, not as builder state.
|
|
>> That is the whole point: in a web app the id is per user or per
|
|
>> session, so it cannot be baked into a singleton ChatClient. 1.x let
|
|
>> you do exactly that, and the result was one shared conversation.
|
|
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.386 s -- in com.ankurm.ai.ChatMemoryMigrationTest
|
|
[INFO]
|
|
[INFO] Results:
|
|
[INFO]
|
|
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
|
|
[INFO]
|
|
[INFO] ------------------------------------------------------------------------
|
|
[INFO] BUILD SUCCESS
|
|
[INFO] ------------------------------------------------------------------------
|
|
[INFO] Total time: 5.266 s
|
|
[INFO] Finished at: 2026-08-01T10:42:58+05:30
|
|
[INFO] ------------------------------------------------------------------------
|