Skip to content

feat: Add S3 chat memory repository implementation#5091

Open
ybezsonov wants to merge 12 commits intospring-projects:mainfrom
ybezsonov:s3-memory
Open

feat: Add S3 chat memory repository implementation#5091
ybezsonov wants to merge 12 commits intospring-projects:mainfrom
ybezsonov:s3-memory

Conversation

@ybezsonov
Copy link
Contributor

@ybezsonov ybezsonov commented Dec 13, 2025

Add S3ChatMemoryRepository with CRUD operations for chat history

Features:

  • Implement S3ChatMemoryConfig with builder pattern and validation
  • Add Spring Boot auto-configuration for integration
  • Support configurable storage class, key prefix, and auto bucket initialization

Test suite (15 integration tests):

  • Full CRUD operations, pagination, conversation replacement
  • Metadata and message order preservation
  • Unicode content, large messages (100KB), special characters handling
  • Auto bucket initialization and error handling
  • Storage class configuration

Documentation:

  • README with usage examples and configuration options

Resolves: #5088

ybezsonov and others added 3 commits January 16, 2026 15:29
- Add S3ChatMemoryRepository with CRUD operations for chat history
- Implement S3ChatMemoryConfig with builder pattern and validation
- Add Spring Boot auto-configuration for integration
- Include test suite (22 tests total):
  - Property-based tests with jqwik for edge cases
  - Integration tests with Testcontainers and LocalStack
  - AutoConfiguration tests for Spring Boot integration
- Add documentation

Resolves: spring-projects#5088
Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
…epository

Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
- Add @nullable annotations to Builder fields in S3ChatMemoryConfig
- Add @nullable to extractConversationId return type in S3ChatMemoryRepository
- Add @NullMarked annotation to package-info.java
- Remove misplaced AutoConfiguration.imports file from core module

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
…module

- Add @NullMarked annotation to package-info.java
- Add @nullable to getBucketName() getter (field without default)
- Remove duplicate maven-compiler-plugin configuration (inherited from parent)

Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
@ybezsonov ybezsonov requested a review from ericbottard January 19, 2026 12:17
@ilayaperumalg ilayaperumalg added this to the 2.0.0.RC1 milestone Jan 20, 2026
…ce notes

- Document AWS SDK for Java 2.x requirement (v1 not supported)
- Add performance note about findConversationIds() pagination at scale
- Use ChatMemory.DEFAULT_CONVERSATION_ID for null/empty conversation IDs

Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
@ybezsonov ybezsonov requested a review from akukuruza February 2, 2026 09:18
Copy link

@akukuruza akukuruza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job! Thank you!

@ilayaperumalg ilayaperumalg modified the milestones: 2.0.0-RC1, 2.0.0-M3 Feb 3, 2026
@akukuruza
Copy link

Hey @ybezsonov, I got one more follow-up question – is there any way to support dynamic key prefixes, or would that be too complex to add?

For example, if we wanted to organize chat history like /memory/{product}/{date}/{conversationId}.json where product and date are determined at runtime based on conversation metadata. Currently, the keyPrefix appears to be static configuration.

This would be helpful for multi-tenant scenarios or when we need to partition history by different business dimensions without creating separate repository instances for each combination.

Thanks!

@ybezsonov
Copy link
Contributor Author

ybezsonov commented Feb 6, 2026

Hey @ybezsonov, I got one more follow-up question – is there any way to support dynamic key prefixes, or would that be too complex to add?

For example, if we wanted to organize chat history like /memory/{product}/{date}/{conversationId}.json where product and date are determined at runtime based on conversation metadata. Currently, the keyPrefix appears to be static configuration.

This would be helpful for multi-tenant scenarios or when we need to partition history by different business dimensions without creating separate repository instances for each combination.

Thanks!

Hi @akukuruza, This is an interesting idea, and multi-tenancy is a very valid use case. I don't know about the date, but I think sessionId could be useful in this case. Some time ago, I implemented short-term and long-term memory with Spring AI https://dev.to/yuriybezsonov/a-practical-guide-to-building-ai-agents-with-java-and-spring-ai-part-2-add-memory-odn and recently updated it to managed memory with AgentCore https://dev.to/yuriybezsonov/ai-agent-memory-made-easy-amazon-bedrock-agentcore-memory-with-spring-ai-3bng.

I will review them all and come up with a solution which should cover multi-tenancy and be ready for different types of memory - similar to namespaces in AgentCore, I think.

@akukuruza
Copy link

Hey @ybezsonov, I got one more follow-up question – is there any way to support dynamic key prefixes, or would that be too complex to add?
For example, if we wanted to organize chat history like /memory/{product}/{date}/{conversationId}.json where product and date are determined at runtime based on conversation metadata. Currently, the keyPrefix appears to be static configuration.
This would be helpful for multi-tenant scenarios or when we need to partition history by different business dimensions without creating separate repository instances for each combination.
Thanks!

Hi @akukuruza, This is an interesting idea, and multi-tenancy is a very valid use case. I don't know about the date, but I think sessionId could be useful in this case. Some time ago, I implemented short-term and long-term memory with Spring AI https://dev.to/yuriybezsonov/a-practical-guide-to-building-ai-agents-with-java-and-spring-ai-part-2-add-memory-odn and recently updated it to managed memory with AgentCore https://dev.to/yuriybezsonov/ai-agent-memory-made-easy-amazon-bedrock-agentcore-memory-with-spring-ai-3bng.

I will review them all and come up with a solution which should cover multi-tenancy and be ready for different types of memory - similar to namespaces in AgentCore, I think.

Appreciate your work on this! The way I see it, adding a Function<String, String> keyResolver to the config would enable users to encode tenant or runtime context into the conversation ID itself, letting the resolver transform it to the proper S3 namespace at runtime.

Add optional keyResolver and conversationIdExtractor functions to
S3ChatMemoryConfig, enabling hierarchical S3 key structures at runtime.
This supports multi-tenant scenarios and business-dimension partitioning
without requiring separate repository instances per namespace.

When neither function is set, existing static keyPrefix behavior is
preserved. When set, both must be provided together (validated at build
time). The keyPrefix continues to serve as the listing base for
findConversationIds().

Signed-off-by: Yuriy Bezsonov <ybezsonov@gmail.com>
@ybezsonov ybezsonov requested a review from akukuruza February 10, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add S3ChatMemoryRepository

5 participants