feat(memory):support self-host mem0#403
Conversation
Change-Id: If8c877e63f8d225d9e88e3830380ec855e7c9fd2 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com>
Change-Id: I1e29c5f25f6cad7231dc77de3157801a98d1e319 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR adds support for self-hosted Mem0 deployments alongside the existing Platform Mem0 support, addressing API incompatibilities between the two deployment types. The implementation introduces an apiType parameter to select the appropriate endpoints and response parsing logic, while also improving robustness with JSON handling.
Key Changes:
- Added
apiTypeparameter toMem0ClientandMem0LongTermMemory.Builderto distinguish between "platform" and "self-hosted" deployments - Implemented conditional endpoint selection and response parsing based on deployment type
- Enhanced forward compatibility by adding
@JsonIgnoreProperties(ignoreUnknown = true)to response classes
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/zh/task/memory.md | Added Chinese documentation explaining apiType parameter usage and configuration for both Platform and self-hosted Mem0 |
| docs/en/task/memory.md | Added English documentation explaining apiType parameter usage and configuration for both Platform and self-hosted Mem0 |
| Mem0Client.java | Added apiType parameter with endpoint selection logic and conditional response parsing for platform vs self-hosted APIs |
| Mem0LongTermMemory.java | Updated builder to accept and pass apiType parameter to Mem0Client |
| Mem0SearchRequest.java | Added userId field with automatic synchronization to filters for v2 API compatibility |
| Mem0AddResponse.java | Added @JsonIgnoreProperties annotation for forward compatibility |
| Mem0SearchResponse.java | Added @JsonIgnoreProperties annotation for forward compatibility |
| Mem0SearchResult.java | Added @JsonIgnoreProperties annotation for forward compatibility |
| Mem0ClientTest.java | Added comprehensive test coverage for self-hosted mode including endpoint verification and response format handling |
| Mem0Example.java | Updated to support apiType configuration via environment variable |
| AutoMemoryExample.java | Added LongTermMemoryMode import and changed sessionId (unrelated to main PR purpose) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Set userId after filters to ensure it's synced | ||
| if (userId != null) { | ||
| request.setUserId(userId); | ||
| } |
There was a problem hiding this comment.
The order of operations in the build() method could lead to data loss. If a user calls builder.filters() to set custom filters (line 228-230), and then also calls builder.userId(), the userId will be added to the builder's filters map at line 257. However, when build() is called, it first sets the filters at line 345 (which includes the userId), then calls setUserId() at line 356, which will try to add userId to filters again (line 190). While this won't cause an error (just overwrites the same key), the more serious issue is if setFilters() is called with a different Map reference, the setUserId call will add userId to that different map, which may not be the intended behavior. Consider either documenting this behavior clearly or restructuring to avoid this potential confusion.
|
|
||
| // Store API type (normalize to lowercase for comparison) | ||
| this.apiType = apiType != null ? apiType.toLowerCase() : "platform"; | ||
|
|
||
| // Select endpoints based on API type | ||
| if ("self-hosted".equals(this.apiType)) { | ||
| this.addEndpoint = SELF_HOSTED_MEMORIES_ENDPOINT; | ||
| this.searchEndpoint = SELF_HOSTED_SEARCH_ENDPOINT; | ||
| } else { | ||
| // Default to platform endpoints | ||
| this.addEndpoint = PLATFORM_MEMORIES_ENDPOINT; | ||
| this.searchEndpoint = PLATFORM_SEARCH_ENDPOINT; | ||
| } |
There was a problem hiding this comment.
The apiType parameter lacks input validation. When an invalid apiType value (neither "platform" nor "self-hosted") is provided, the code silently defaults to platform endpoints without any warning or error. This could lead to confusion during debugging if a user mistypes the apiType. Consider adding validation to reject invalid apiType values or at minimum log a warning when an unrecognized value is provided.
| /** | ||
| * Sets the Mem0 API type. | ||
| * | ||
| * @param apiType API type: "platform" for Platform Mem0 (default), | ||
| * "self-hosted" for self-hosted Mem0 | ||
| * @return This builder | ||
| */ | ||
| public Builder apiType(String apiType) { | ||
| this.apiType = apiType; | ||
| return this; | ||
| } | ||
|
|
There was a problem hiding this comment.
The new apiType parameter added to Mem0LongTermMemory.Builder lacks test coverage. While Mem0ClientTest has comprehensive tests for self-hosted mode, there are no tests in Mem0LongTermMemoryTest that verify the apiType parameter is correctly passed to Mem0Client. Consider adding tests to ensure that when apiType is set to "self-hosted" or "platform" in Mem0LongTermMemory.Builder, it properly configures the underlying Mem0Client with the correct endpoints.
| public String getUserId() { | ||
| return userId; | ||
| } | ||
|
|
||
| public void setUserId(String userId) { | ||
| this.userId = userId; | ||
| // Automatically sync to filters if set | ||
| if (userId != null && filters != null) { |
There was a problem hiding this comment.
There's a potential NullPointerException if filters is null when setUserId is called. The method checks if userId is not null and filters is not null before adding to filters, but the filters field could potentially be null if not properly initialized. Consider adding a null-safety check or initializing filters if it's null, similar to what's done in the Builder.userId() method.
| public String getUserId() { | |
| return userId; | |
| } | |
| public void setUserId(String userId) { | |
| this.userId = userId; | |
| // Automatically sync to filters if set | |
| if (userId != null && filters != null) { | |
| /** | |
| * Returns the user identifier associated with this search request. | |
| * | |
| * @return the user identifier, or {@code null} if not set | |
| */ | |
| public String getUserId() { | |
| return userId; | |
| } | |
| /** | |
| * Sets the user identifier and synchronizes it into the filters map. | |
| * | |
| * @param userId the user identifier; if non-null it will be stored in the filters map | |
| */ | |
| public void setUserId(String userId) { | |
| this.userId = userId; | |
| // Automatically sync to filters if set | |
| if (userId != null) { | |
| if (filters == null) { | |
| filters = new HashMap<>(); | |
| } |
| } catch (IOException e) { | ||
| // Re-throw IOException as-is (it already contains status code info) | ||
| throw e; | ||
| } catch (Exception e) { | ||
| // Wrap other exceptions | ||
| throw new IOException("Mem0 API " + operationName + " failed", e); |
There was a problem hiding this comment.
The added catch blocks at lines 176-181 are redundant and don't add value. The IOException catch at line 176 re-throws the exception as-is without any transformation, and the generic Exception catch wraps exceptions that are already being thrown by the try-with-resources block. The original code already handles IOException appropriately through the Mono error handling. These catch blocks should be removed to simplify the code.
| } catch (IOException e) { | |
| // Re-throw IOException as-is (it already contains status code info) | |
| throw e; | |
| } catch (Exception e) { | |
| // Wrap other exceptions | |
| throw new IOException("Mem0 API " + operationName + " failed", e); |
| .hook(new AutoContextHook()) // Register the hook for automatic setup | ||
| .build(); | ||
| String sessionId = "session111111111111"; | ||
| String sessionId = "123453344"; |
There was a problem hiding this comment.
The sessionId value has been changed from "session111111111111" to "123453344" without any clear reason in the PR description or comments. This appears to be an unrelated change that doesn't align with the stated purpose of this PR (adding self-hosted Mem0 support). If this is an intentional change, it should be documented or made in a separate commit.
| String sessionId = "123453344"; | |
| String sessionId = "session111111111111"; |
|
|
||
| import io.agentscope.core.ReActAgent; | ||
| import io.agentscope.core.formatter.dashscope.DashScopeChatFormatter; | ||
| import io.agentscope.core.memory.LongTermMemoryMode; |
There was a problem hiding this comment.
The import of LongTermMemoryMode appears to be added but was likely already being used in the file (at line 82 where .longTermMemoryMode(LongTermMemoryMode.STATIC_CONTROL) is called). However, if this import was missing before and is now being added, this is a bug fix rather than part of the self-hosted Mem0 feature. This should be clarified or separated into its own fix.
Change-Id: I903c3f79433843b9793e1a59b859ccc8e9b3c6b7 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com>
This pull request improves support for both platform and self-hosted Mem0 deployments in the long-term memory extension, making the API more flexible and robust. The main changes include allowing users to specify the Mem0 API type, updating endpoint handling in the client, and enhancing search request capabilities.
Mem0 API deployment flexibility and configuration:
The
Mem0LongTermMemorybuilder andMem0Clientnow accept anapiTypeparameter, allowing users to specify"platform"(default) or"self-hosted"deployments. Endpoints are selected accordingly, and example code and documentation have been updated to reflect this. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]The
Mem0Clientclass now chooses the correct API endpoints for memory add/search operations based on theapiType, supporting both platform and self-hosted Mem0. [1] [2] [3] [4] [5]Robustness and compatibility improvements:
The
Mem0Clientsearch method now handles differences in response format between platform and self-hosted Mem0. It parses results accordingly to ensure compatibility. [1] [2]The
Mem0AddResponseandMem0SearchResponseclasses now ignore unknown JSON properties, increasing forward compatibility with future API changes. [1] [2] [3] [4]Search request enhancements:
Mem0SearchRequestclass and its builder now support auserIdfield, which is automatically added to filters for compatibility with the v2 API. [1] [2] [3] [4] [5]These changes make the Mem0 extension more flexible, easier to configure for different deployment scenarios, and more robust to API changes.Change-Id: If8c877e63f8d225d9e88e3830380ec855e7c9fd2
Co-developed-by: Aone Copilot noreply@alibaba-inc.com
AgentScope-Java Version
[The version of AgentScope-Java you are working on, e.g. 1.0.4, check your pom.xml dependency version or run
mvn dependency:tree | grep agentscope-parent:pom(only mac/linux)]Description
[Please describe the background, purpose, changes made, and how to test this PR]
Checklist
Please check the following items before code is ready to be reviewed.
mvn spotless:applymvn test)