Skip to content

Commit 873ca2d

Browse files
authored
feat: support metadata recording and filtering for Mem0 (#563)
## AgentScope-Java Version [The version of AgentScope-Java you are working on, e.g. 1.0.7, check your pom.xml dependency version or run `mvn dependency:tree | grep agentscope-parent:pom`(only mac/linux)] $ mvn dependency:tree | grep agentscope-parent:pom [INFO] io.agentscope:agentscope-parent:pom:1.0.8-SNAPSHOT ## Description [Please describe the background, purpose, changes made, and how to test this PR] Feat [#561 ](#561) ## Checklist Please check the following items before code is ready to be reviewed. - [x] Code has been formatted with `mvn spotless:apply` - [x] All tests are passing (`mvn test`) - [x] Javadoc comments are complete and follow project conventions - [x] Related documentation has been updated (e.g. links, examples, etc.) - [x] Code is ready for review
1 parent dc35bc5 commit 873ca2d

File tree

3 files changed

+102
-14
lines changed

3 files changed

+102
-14
lines changed

agentscope-examples/advanced/src/main/java/io/agentscope/examples/advanced/Mem0Example.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import io.agentscope.core.memory.mem0.Mem0LongTermMemory;
2323
import io.agentscope.core.message.Msg;
2424
import io.agentscope.core.model.DashScopeChatModel;
25+
import java.util.HashMap;
26+
import java.util.Map;
2527

2628
/**
2729
* Mem0Example - Demonstrates long-term memory using Mem0 backend.
@@ -33,14 +35,16 @@ public static void main(String[] args) throws Exception {
3335
String dashscopeApiKey = ExampleUtils.getDashScopeApiKey();
3436
String mem0BaseUrl = getMem0BaseUrl();
3537
Mem0ApiType mem0ApiType = getMem0ApiType();
36-
38+
Map<String, Object> metadata = new HashMap<>();
39+
metadata.put("agentName", "SmartAssistant");
3740
Mem0LongTermMemory.Builder memoryBuilder =
3841
Mem0LongTermMemory.builder()
3942
.agentName("SmartAssistant")
4043
.userId("static-control01126")
4144
.apiBaseUrl(mem0BaseUrl)
4245
.apiKey(System.getenv("MEM0_API_KEY"))
43-
.apiType(mem0ApiType);
46+
.apiType(mem0ApiType)
47+
.metadata(metadata);
4448

4549
Mem0LongTermMemory longTermMemory = memoryBuilder.build();
4650

agentscope-extensions/agentscope-extensions-mem0/src/main/java/io/agentscope/core/memory/mem0/Mem0LongTermMemory.java

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.agentscope.core.memory.LongTermMemory;
1919
import io.agentscope.core.message.Msg;
2020
import java.util.List;
21+
import java.util.Map;
2122
import java.util.Objects;
2223
import java.util.stream.Collectors;
2324
import reactor.core.publisher.Mono;
@@ -34,6 +35,7 @@
3435
* <li>Semantic memory search using vector embeddings
3536
* <li>LLM-powered memory extraction and inference
3637
* <li>Multi-tenant memory isolation (agent, user, run)
38+
* <li>Custom metadata support for tagging and filtering memories
3739
* <li>Automatic fallback mechanisms to ensure reliable memory storage
3840
* <li>Reactive, non-blocking operations
3941
* </ul>
@@ -44,6 +46,7 @@
4446
* <li><b>agentId:</b> Identifies the agent (optional)</li>
4547
* <li><b>userId:</b> Identifies the user/workspace (optional)</li>
4648
* <li><b>runId:</b> Identifies the session/run (optional)</li>
49+
* <li><b>metadata:</b> Custom key-value pairs for additional filtering (optional)</li>
4750
* </ul>
4851
* At least one identifier is required. During retrieval, only memories with matching
4952
* metadata are returned.
@@ -53,35 +56,47 @@
5356
* // Create memory instance with authentication
5457
* Mem0LongTermMemory memory = Mem0LongTermMemory.builder()
5558
* .agentName("Assistant")
56-
* .userName("user_123")
59+
* .userId("user_123")
5760
* .apiBaseUrl("http://localhost:8000")
5861
* .apiKey(System.getenv("MEM0_API_KEY"))
5962
* .build();
6063
*
6164
* // For local deployments without authentication
6265
* Mem0LongTermMemory localMemory = Mem0LongTermMemory.builder()
6366
* .agentName("Assistant")
64-
* .userName("user_123")
67+
* .userId("user_123")
6568
* .apiBaseUrl("http://localhost:8000")
6669
* .build();
6770
*
6871
* // For self-hosted Mem0
6972
* Mem0LongTermMemory selfHostedMemory = Mem0LongTermMemory.builder()
7073
* .agentName("Assistant")
71-
* .userName("user_123")
74+
* .userId("user_123")
7275
* .apiBaseUrl("http://localhost:8000")
7376
* .apiType(Mem0ApiType.SELF_HOSTED) // Specify self-hosted API type
7477
* .build();
7578
*
76-
* // Record a message
79+
* // With custom metadata for filtering
80+
* Map<String, Object> metadata = new HashMap<>();
81+
* metadata.put("category", "travel");
82+
* metadata.put("project_id", "proj_001");
83+
*
84+
* Mem0LongTermMemory memoryWithMetadata = Mem0LongTermMemory.builder()
85+
* .agentName("Assistant")
86+
* .userId("user_123")
87+
* .apiBaseUrl("http://localhost:8000")
88+
* .metadata(metadata) // Custom metadata for storage and filtering
89+
* .build();
90+
*
91+
* // Record a message (metadata will be stored with the memory)
7792
* Msg msg = Msg.builder()
7893
* .role(MsgRole.USER)
7994
* .content("I prefer homestays when traveling")
8095
* .build();
8196
*
8297
* memory.record(List.of(msg)).block();
8398
*
84-
* // Retrieve relevant memories
99+
* // Retrieve relevant memories (metadata will be used as filter)
85100
* Msg query = Msg.builder()
86101
* .role(MsgRole.USER)
87102
* .content("What are my travel preferences?")
@@ -101,6 +116,24 @@ public class Mem0LongTermMemory implements LongTermMemory {
101116
private final String userId;
102117
private final String runId;
103118

119+
/**
120+
* Custom metadata to be stored with memories and used for filtering during retrieval.
121+
*
122+
* <p>This metadata is:
123+
* <ul>
124+
* <li>Included in the {@code metadata} field when recording memories via {@link #record(List)}</li>
125+
* <li>Added to the {@code filters} field when retrieving memories via {@link #retrieve(Msg)}</li>
126+
* </ul>
127+
*
128+
* <p>Use cases include:
129+
* <ul>
130+
* <li>Tagging memories with custom labels (e.g., "category": "travel")</li>
131+
* <li>Filtering memories by project, tenant, or other business attributes</li>
132+
* <li>Storing additional context that should be associated with all memories</li>
133+
* </ul>
134+
*/
135+
private final Map<String, Object> metadata;
136+
104137
/**
105138
* Private constructor - use Builder instead.
106139
*/
@@ -110,6 +143,7 @@ private Mem0LongTermMemory(Builder builder) {
110143
this.agentId = builder.agentName;
111144
this.userId = builder.userId;
112145
this.runId = builder.runName;
146+
this.metadata = builder.metadata;
113147

114148
// Validate that at least one identifier is provided
115149
if (agentId == null && userId == null && runId == null) {
@@ -161,6 +195,7 @@ public Mono<Void> record(List<Msg> msgs) {
161195
.agentId(agentId)
162196
.userId(userId)
163197
.runId(runId)
198+
.metadata(metadata)
164199
.infer(true)
165200
.build();
166201

@@ -191,17 +226,30 @@ private Mem0Message convertToMem0Message(Msg msg) {
191226
/**
192227
* Builds a search request with the given query.
193228
*
229+
* <p>The search request includes:
230+
* <ul>
231+
* <li>Standard filters: userId, agentId, runId (added by builder convenience methods)</li>
232+
* <li>Custom metadata filters: merged into filters via builder.getFilters()</li>
233+
* </ul>
234+
*
194235
* @param query The search query string
195236
* @return A configured Mem0SearchRequest for v2 API
196237
*/
197238
private Mem0SearchRequest buildSearchRequest(String query) {
198-
return Mem0SearchRequest.builder()
199-
.query(query)
200-
.userId(userId)
201-
.agentId(agentId)
202-
.runId(runId)
203-
.topK(5)
204-
.build();
239+
Mem0SearchRequest.Builder builder =
240+
Mem0SearchRequest.builder()
241+
.query(query)
242+
.userId(userId)
243+
.agentId(agentId)
244+
.runId(runId)
245+
.topK(5);
246+
247+
// Merge custom metadata into filters if present
248+
if (metadata != null && !metadata.isEmpty()) {
249+
builder.getFilters().putAll(metadata);
250+
}
251+
252+
return builder.build();
205253
}
206254

207255
/**
@@ -262,6 +310,7 @@ public static class Builder {
262310
private String apiKey;
263311
private Mem0ApiType apiType;
264312
private java.time.Duration timeout = java.time.Duration.ofSeconds(60);
313+
private Map<String, Object> metadata;
265314

266315
/**
267316
* Sets the agent name identifier.
@@ -341,6 +390,37 @@ public Builder apiType(Mem0ApiType apiType) {
341390
return this;
342391
}
343392

393+
/**
394+
* Sets custom metadata to be stored with memories and used for filtering.
395+
*
396+
* <p>This metadata will be:
397+
* <ul>
398+
* <li>Included in the request body when recording memories</li>
399+
* <li>Added to the filters when searching/retrieving memories</li>
400+
* </ul>
401+
*
402+
* <p>Example usage:
403+
* <pre>{@code
404+
* Map<String, Object> metadata = new HashMap<>();
405+
* metadata.put("category", "travel");
406+
* metadata.put("priority", "high");
407+
*
408+
* Mem0LongTermMemory memory = Mem0LongTermMemory.builder()
409+
* .agentName("Assistant")
410+
* .userId("user_123")
411+
* .apiBaseUrl("http://localhost:8000")
412+
* .metadata(metadata)
413+
* .build();
414+
* }</pre>
415+
*
416+
* @param metadata Custom metadata map (can be null)
417+
* @return This builder
418+
*/
419+
public Builder metadata(Map<String, Object> metadata) {
420+
this.metadata = metadata;
421+
return this;
422+
}
423+
344424
/**
345425
* Builds the Mem0LongTermMemory instance.
346426
*

agentscope-extensions/agentscope-extensions-mem0/src/main/java/io/agentscope/core/memory/mem0/Mem0SearchRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ public Builder filters(Map<String, Object> filters) {
230230
return this;
231231
}
232232

233+
public Map<String, Object> getFilters() {
234+
return this.filters;
235+
}
236+
233237
/**
234238
* Convenience method to add agent_id to filters.
235239
*

0 commit comments

Comments
 (0)