Files
spring-ai/chat-memory/README.md
T
Claude 823f6fac5b Add chat-memory module: MessageWindowChatMemory, JDBC and Redis repositories, per-user conversation IDs
Real PostgreSQL 16 and Redis Stack 7.4; 24 tests write output/01-24. Covers the 20-message
default window with no property, the 36-character conversation_id, tool messages dropped by
JdbcChatMemoryRepository, concurrent add() on one conversation, a 1.x table under the 2.0
repository, the Redis repository silently backing off for a custom ChatMemory, and the
removal of PromptChatMemoryAdvisor.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ja4jkzrbQ4LQZBNrb5mkZE
2026-09-24 15:56:47 +00:00

4.6 KiB

chat-memory

Companion code for Chat Memory in Spring AI 2.0: JDBC, Redis and Windowed Conversations, part of the Spring AI series on ankurm.com.

MessageChatMemoryAdvisor + MessageWindowChatMemory + a ChatMemoryRepository (in-memory, PostgreSQL through JDBC, or Redis Stack), with per-user conversation IDs, a token-budget memory of our own, and every trap the article describes reproduced by a test that writes its own transcript.

There is no live model anywhere: ScriptedChatModel records the prompts it is sent, which is what a memory test needs to prove -- which messages reached the model. The databases are real.

Versions

Component Version
Spring Boot 4.1.1
Spring AI 2.0.1 (GA 2026-06-12 for 2.0.0; 2.0.1 on Maven Central 2026-08-20)
PostgreSQL 16.13
Redis Stack 7.4.7 (RediSearch 2.10.20, RedisJSON)
Jedis (pulled in by the Redis starter) 7.4.1
Java 25 (LTS)

Quickstart

scripts/services-up.sh     # PostgreSQL 16 (apt), Redis Stack (docker), plain Redis (apt) -- optional
scripts/run-all.sh         # runs all 24 tests and regenerates output/01 .. 24

Tests that need a service which is not listening are skipped with a message, not failed, so the in-memory tests still run with nothing installed. Run the app itself with mvn spring-boot:run (in-memory), -Dspring-boot.run.profiles=jdbc or =redis; it needs OPENAI_API_KEY for real answers.

What's here

File What it shows
config/MemoryConfig.java The ChatMemory bean (window size is not configurable by property) and the ChatClient with MessageChatMemoryAdvisor
config/RedisMemoryConfig.java Why the Redis repository is built by hand: the autoconfigured one silently steps aside for a custom ChatMemory
memory/ConversationRegistry.java Who owns which conversation; app.ownership.enforce=false reproduces the leak
memory/ConversationService.java Passing ChatMemory.CONVERSATION_ID per request
memory/TokenBudgetChatMemory.java A ChatMemory that prunes by tokens instead of by message count
web/ChatController.java Four endpoints; the user comes from an X-User header only so the tests need no login
application*.yml Profiles jdbc and redis; the default excludes both repository autoconfigurations
src/broken/PromptAdvisorFrom1x.java Not compiled by the build; scripts/capture-1x-compile.sh compiles it against 2.0.1 and 1.1.8

Output files

Every file is written by the test named in the right column (or by the script), and every console block in the article is quoted from one of them.

File Written by
01-window-semantics.txt WindowSemanticsTest
02-advisor-conversations.txt, 03-missing-conversation-id.txt AdvisorConversationTest
04-autoconfigured-defaults.txt, 05-no-window-property.txt DefaultWindowTest
06-token-growth.txt TokenGrowthTest
07-token-budget-memory.txt TokenBudgetChatMemoryTest
08 .. 14 (JDBC round trip, 36-character limit, dropped tool messages, lost update, overlapping saves, 1.x table upgrade, locked fix) JdbcPostgresTest
15-redis-round-trip.txt, 16-redis-lost-update.txt RedisStackTest
17-redis-silent-fallback.txt RedisSilentFallbackTest
18-redis-ttl-and-cap.txt RedisCapsAndTtlTest
19-redis-plain-fails.txt RedisPlainFailureTest
20-ownership-enforced.txt, 21-ownership-unenforced.txt OwnershipTest, OwnershipLeakTest
22-config-keys.txt ConfigKeysTest
23-prompt-advisor-removed.txt scripts/capture-1x-compile.sh
24-memory-with-tool-calling.txt ToolCallingMemoryTest

Two consecutive mvn test runs produce byte-identical files. 12-jdbc-overlapping-saves.txt deliberately prints no exact count: two saveAll calls that overlap produce 3 or 6 messages depending on timing, and the file says so instead of pinning one run.

Requirements

JDK 25, Maven. PostgreSQL 16 and Redis Stack are optional (see above); Docker is only used to run Redis Stack.