# Spring AI 1.x → 2.0 migration Companion repository for **[Spring AI 1.x to 2.0: The Migration Guide](https://ankurm.com/spring-ai-1-to-2-migration-guide/)** on [ankurm.com](https://ankurm.com). Verified against **Spring AI 2.0.0**, **Spring Boot 4.1.0**, **JDK 25.0.3**. Full output in [`run.txt`](run.txt). --- ## Quick start **No API key, no network, no provider account.** A canned `ChatModel` returns fixed responses, which is enough to exercise every API that changed — `ChatClient`, advisors and chat memory all sit *above* the model. ```console git clone https://ankurm.com/git.app/asmhatre/spring-ai-2-migration.git cd spring-ai-2-migration mvn test ``` 5 tests, under a second. --- ## What is here | | | |---|---| | [Breaking-change reference](docs/01-breaking-changes.md) | The complete list, organised by area, with the migration order that works | | [`ChatMemoryMigrationTest`](src/test/java/com/ankurm/ai/ChatMemoryMigrationTest.java) | The chat-memory changes, asserted — including a test proving two conversations cannot see each other | | [`TestChatModel`](src/main/java/com/ankurm/ai/TestChatModel.java) | The offline model. Records every `Prompt` it receives, so tests assert **what Spring AI sent**, not what a model replied | --- ## The five things most likely to bite 1. **Boot 4 is mandatory.** Spring AI 2.0 is built on the Boot 4 dependency model and *cannot* load in a Boot 3 context. Migrate Boot first, as a separate step. 2. **Jackson 2 → 3** (`com.fasterxml.jackson` → `tools.jackson`), which changes default date format and property order. This is the change least likely to be caught by a compiler and most likely to break a downstream consumer. 3. **`internalToolExecutionEnabled` is removed** — not renamed. Per-model internal tool execution no longer exists in any `ChatModel`, so if you relied on the model driving the tool loop, that orchestration moves up into the advisor layer. 4. **Chat memory gets stricter.** 2.0 removes the remaining default-ID and builder-based configuration paths — `ChatMemory.DEFAULT_CONVERSATION_ID` and `.conversationId()` are both gone. Explicit per-request conversation IDs, introduced during the 1.x line, become the only safe migration target. Leaning on the old default put every caller in one shared conversation. 5. **Structured-output schema changes are silent.** Optional Kotlin properties and `@JsonProperty` without an explicit `required` are no longer in the JSON Schema `required` array, which changes what the model returns without producing any error. --- ## A trap worth its own line `ChatMemory.CONVERSATION_ID` still exists. `ChatMemory.DEFAULT_CONVERSATION_ID` does not. They look like the same thing and are opposites: the survivor is the **metadata key** you use to pass an id, the removed one was the **value** `"default"` used when you passed nothing. Swapping one for the other to clear a compile error restores exactly the shared-conversation behaviour that removing it was meant to take away. --- ## Licence MIT. See [LICENSE](LICENSE).