mem0ai 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,8 +29,19 @@ var MemoryConfigSchema = z.object({
29
29
  }),
30
30
  historyDbPath: z.string().optional(),
31
31
  customPrompt: z.string().optional(),
32
+ enableGraph: z.boolean().optional(),
32
33
  graphStore: z.object({
33
- config: z.any().optional()
34
+ provider: z.string(),
35
+ config: z.object({
36
+ url: z.string(),
37
+ username: z.string(),
38
+ password: z.string()
39
+ }),
40
+ llm: z.object({
41
+ provider: z.string(),
42
+ config: z.record(z.string(), z.any())
43
+ }).optional(),
44
+ customPrompt: z.string().optional()
34
45
  }).optional()
35
46
  });
36
47
 
@@ -62,25 +73,43 @@ import OpenAI2 from "openai";
62
73
  var OpenAILLM = class {
63
74
  constructor(config) {
64
75
  this.openai = new OpenAI2({ apiKey: config.apiKey });
65
- this.model = config.model || "gpt-4-turbo-preview";
76
+ this.model = config.model || "gpt-4o-mini";
66
77
  }
67
- async generateResponse(messages, responseFormat) {
78
+ async generateResponse(messages, responseFormat, tools) {
68
79
  const completion = await this.openai.chat.completions.create({
69
- messages: messages.map((msg) => ({
70
- role: msg.role,
71
- content: msg.content
72
- })),
80
+ messages: messages.map((msg) => {
81
+ const role = msg.role;
82
+ return {
83
+ role,
84
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
85
+ };
86
+ }),
73
87
  model: this.model,
74
- response_format: responseFormat
88
+ response_format: responseFormat,
89
+ ...tools && { tools, tool_choice: "auto" }
75
90
  });
76
- return completion.choices[0].message.content || "";
91
+ const response = completion.choices[0].message;
92
+ if (response.tool_calls) {
93
+ return {
94
+ content: response.content || "",
95
+ role: response.role,
96
+ toolCalls: response.tool_calls.map((call) => ({
97
+ name: call.function.name,
98
+ arguments: call.function.arguments
99
+ }))
100
+ };
101
+ }
102
+ return response.content || "";
77
103
  }
78
104
  async generateChat(messages) {
79
105
  const completion = await this.openai.chat.completions.create({
80
- messages: messages.map((msg) => ({
81
- role: msg.role,
82
- content: msg.content
83
- })),
106
+ messages: messages.map((msg) => {
107
+ const role = msg.role;
108
+ return {
109
+ role,
110
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
111
+ };
112
+ }),
84
113
  model: this.model
85
114
  });
86
115
  const response = completion.choices[0].message;
@@ -95,37 +124,57 @@ var OpenAILLM = class {
95
124
  import OpenAI3 from "openai";
96
125
  var OpenAIStructuredLLM = class {
97
126
  constructor(config) {
98
- const apiKey = config.apiKey || process.env.OPENAI_API_KEY;
99
- if (!apiKey) {
100
- throw new Error("OpenAI API key is required");
101
- }
102
- const baseUrl = process.env.OPENAI_API_BASE || "https://api.openai.com/v1";
103
- this.client = new OpenAI3({ apiKey, baseURL: baseUrl });
104
- this.model = config.model || "gpt-4-0125-preview";
127
+ this.openai = new OpenAI3({ apiKey: config.apiKey });
128
+ this.model = config.model || "gpt-4-turbo-preview";
105
129
  }
106
- async generateResponse(messages, responseFormat) {
107
- const response = await this.client.chat.completions.create({
108
- model: this.model,
130
+ async generateResponse(messages, responseFormat, tools) {
131
+ const completion = await this.openai.chat.completions.create({
109
132
  messages: messages.map((msg) => ({
110
133
  role: msg.role,
111
- content: msg.content
134
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
112
135
  })),
113
- response_format: responseFormat
136
+ model: this.model,
137
+ ...tools ? {
138
+ tools: tools.map((tool) => ({
139
+ type: "function",
140
+ function: {
141
+ name: tool.function.name,
142
+ description: tool.function.description,
143
+ parameters: tool.function.parameters
144
+ }
145
+ })),
146
+ tool_choice: "auto"
147
+ } : responseFormat ? {
148
+ response_format: {
149
+ type: responseFormat.type
150
+ }
151
+ } : {}
114
152
  });
115
- return response.choices[0].message.content || "";
153
+ const response = completion.choices[0].message;
154
+ if (response.tool_calls) {
155
+ return {
156
+ content: response.content || "",
157
+ role: response.role,
158
+ toolCalls: response.tool_calls.map((call) => ({
159
+ name: call.function.name,
160
+ arguments: call.function.arguments
161
+ }))
162
+ };
163
+ }
164
+ return response.content || "";
116
165
  }
117
166
  async generateChat(messages) {
118
- const response = await this.client.chat.completions.create({
119
- model: this.model,
167
+ const completion = await this.openai.chat.completions.create({
120
168
  messages: messages.map((msg) => ({
121
169
  role: msg.role,
122
- content: msg.content
123
- }))
170
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
171
+ })),
172
+ model: this.model
124
173
  });
125
- const message = response.choices[0].message;
174
+ const response = completion.choices[0].message;
126
175
  return {
127
- content: message.content || "",
128
- role: message.role
176
+ content: response.content || "",
177
+ role: response.role
129
178
  };
130
179
  }
131
180
  };
@@ -148,9 +197,9 @@ var AnthropicLLM = class {
148
197
  model: this.model,
149
198
  messages: otherMessages.map((msg) => ({
150
199
  role: msg.role,
151
- content: msg.content
200
+ content: typeof msg.content === "string" ? msg.content : msg.content.image_url.url
152
201
  })),
153
- system: systemMessage == null ? void 0 : systemMessage.content,
202
+ system: typeof (systemMessage == null ? void 0 : systemMessage.content) === "string" ? systemMessage.content : void 0,
154
203
  max_tokens: 4096
155
204
  });
156
205
  return response.content[0].text;
@@ -180,7 +229,7 @@ var GroqLLM = class {
180
229
  model: this.model,
181
230
  messages: messages.map((msg) => ({
182
231
  role: msg.role,
183
- content: msg.content
232
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
184
233
  })),
185
234
  response_format: responseFormat
186
235
  });
@@ -191,7 +240,7 @@ var GroqLLM = class {
191
240
  model: this.model,
192
241
  messages: messages.map((msg) => ({
193
242
  role: msg.role,
194
- content: msg.content
243
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
195
244
  }))
196
245
  });
197
246
  const message = response.choices[0].message;
@@ -966,7 +1015,7 @@ var EmbedderFactory = class {
966
1015
  };
967
1016
  var LLMFactory = class {
968
1017
  static create(provider, config) {
969
- switch (provider.toLowerCase()) {
1018
+ switch (provider) {
970
1019
  case "openai":
971
1020
  return new OpenAILLM(config);
972
1021
  case "openai_structured":
@@ -1032,7 +1081,7 @@ function getFactRetrievalMessages(parsedMessages) {
1032
1081
  Input: Me favourite movies are Inception and Interstellar.
1033
1082
  Output: {"facts" : ["Favourite movies are Inception and Interstellar"]}
1034
1083
 
1035
- Return the facts and preferences in a json format as shown above.
1084
+ Return the facts and preferences in a JSON format as shown above. You MUST return a valid JSON object with a 'facts' key containing an array of strings.
1036
1085
 
1037
1086
  Remember the following:
1038
1087
  - Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.
@@ -1041,16 +1090,16 @@ function getFactRetrievalMessages(parsedMessages) {
1041
1090
  - If the user asks where you fetched my information, answer that you found from publicly available sources on internet.
1042
1091
  - If you do not find anything relevant in the below conversation, you can return an empty list corresponding to the "facts" key.
1043
1092
  - Create the facts based on the user and assistant messages only. Do not pick anything from the system messages.
1044
- - Make sure to return the response in the format mentioned in the examples. The response should be in json with a key as "facts" and corresponding value will be a list of strings.
1093
+ - Make sure to return the response in the JSON format mentioned in the examples. The response should be in JSON with a key as "facts" and corresponding value will be a list of strings.
1045
1094
  - DO NOT RETURN ANYTHING ELSE OTHER THAN THE JSON FORMAT.
1046
- - DO NOT ADD ANY ADDITIONAL TEXT OR CODEBLOCK IN THE JSON FIELDS WHICH MAKE IT INVALUD SUCH AS "\`\`\`json" OR "\`\`\`".
1095
+ - DO NOT ADD ANY ADDITIONAL TEXT OR CODEBLOCK IN THE JSON FIELDS WHICH MAKE IT INVALID SUCH AS "\`\`\`json" OR "\`\`\`".
1047
1096
  - You should detect the language of the user input and record the facts in the same language.
1048
1097
  - For basic factual statements, break them down into individual facts if they contain multiple pieces of information.
1049
1098
 
1050
- Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the user, if any, from the conversation and return them in the json format as shown above.
1099
+ Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the user, if any, from the conversation and return them in the JSON format as shown above.
1051
1100
  You should detect the language of the user input and record the facts in the same language.
1052
1101
  `;
1053
- const userPrompt = `Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the user, if any, from the conversation and return them in the json format as shown above.
1102
+ const userPrompt = `Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the user, if any, from the conversation and return them in the JSON format as shown above.
1054
1103
 
1055
1104
  Input:
1056
1105
  ${parsedMessages}`;
@@ -1213,14 +1262,14 @@ function getUpdateMemoryMessages(retrievedOldMemory, newRetrievedFacts) {
1213
1262
  ${JSON.stringify(newRetrievedFacts, null, 2)}
1214
1263
 
1215
1264
  Follow the instruction mentioned below:
1216
- - Do not return anything from the custom few shot prompts provided above.
1265
+ - Do not return anything from the custom few shot example prompts provided above.
1217
1266
  - If the current memory is empty, then you have to add the new retrieved facts to the memory.
1218
1267
  - You should return the updated memory in only JSON format as shown below. The memory key should be the same if no changes are made.
1219
1268
  - If there is an addition, generate a new key and add the new memory corresponding to it.
1220
1269
  - If there is a deletion, the memory key-value pair should be removed from the memory.
1221
1270
  - If there is an update, the ID key should remain the same and only the value needs to be updated.
1222
1271
  - DO NOT RETURN ANYTHING ELSE OTHER THAN THE JSON FORMAT.
1223
- - DO NOT ADD ANY ADDITIONAL TEXT OR CODEBLOCK IN THE JSON FIELDS WHICH MAKE IT INVALUD SUCH AS "\`\`\`json" OR "\`\`\`".
1272
+ - DO NOT ADD ANY ADDITIONAL TEXT OR CODEBLOCK IN THE JSON FIELDS WHICH MAKE IT INVALID SUCH AS "\`\`\`json" OR "\`\`\`".
1224
1273
 
1225
1274
  Do not return anything except the JSON format.`;
1226
1275
  }
@@ -1320,6 +1369,21 @@ var DEFAULT_MEMORY_CONFIG = {
1320
1369
  model: "gpt-4-turbo-preview"
1321
1370
  }
1322
1371
  },
1372
+ enableGraph: false,
1373
+ graphStore: {
1374
+ provider: "neo4j",
1375
+ config: {
1376
+ url: process.env.NEO4J_URL || "neo4j://localhost:7687",
1377
+ username: process.env.NEO4J_USERNAME || "neo4j",
1378
+ password: process.env.NEO4J_PASSWORD || "password"
1379
+ },
1380
+ llm: {
1381
+ provider: "openai",
1382
+ config: {
1383
+ model: "gpt-4-turbo-preview"
1384
+ }
1385
+ }
1386
+ },
1323
1387
  historyDbPath: "memory.db"
1324
1388
  };
1325
1389
 
@@ -1353,12 +1417,779 @@ var ConfigManager = class {
1353
1417
  },
1354
1418
  historyDbPath: userConfig.historyDbPath || DEFAULT_MEMORY_CONFIG.historyDbPath,
1355
1419
  customPrompt: userConfig.customPrompt,
1356
- graphStore: userConfig.graphStore
1420
+ graphStore: {
1421
+ ...DEFAULT_MEMORY_CONFIG.graphStore,
1422
+ ...userConfig.graphStore
1423
+ },
1424
+ enableGraph: userConfig.enableGraph || DEFAULT_MEMORY_CONFIG.enableGraph
1357
1425
  };
1358
1426
  return MemoryConfigSchema.parse(mergedConfig);
1359
1427
  }
1360
1428
  };
1361
1429
 
1430
+ // src/oss/src/memory/graph_memory.ts
1431
+ import neo4j from "neo4j-driver";
1432
+
1433
+ // src/oss/src/utils/bm25.ts
1434
+ var BM25 = class {
1435
+ constructor(documents, k1 = 1.5, b = 0.75) {
1436
+ this.documents = documents;
1437
+ this.k1 = k1;
1438
+ this.b = b;
1439
+ this.docLengths = documents.map((doc) => doc.length);
1440
+ this.avgDocLength = this.docLengths.reduce((a, b2) => a + b2, 0) / documents.length;
1441
+ this.docFreq = /* @__PURE__ */ new Map();
1442
+ this.idf = /* @__PURE__ */ new Map();
1443
+ this.computeIdf();
1444
+ }
1445
+ computeIdf() {
1446
+ const N = this.documents.length;
1447
+ for (const doc of this.documents) {
1448
+ const terms = new Set(doc);
1449
+ for (const term of terms) {
1450
+ this.docFreq.set(term, (this.docFreq.get(term) || 0) + 1);
1451
+ }
1452
+ }
1453
+ for (const [term, freq] of this.docFreq) {
1454
+ this.idf.set(term, Math.log((N - freq + 0.5) / (freq + 0.5) + 1));
1455
+ }
1456
+ }
1457
+ score(query, doc, index) {
1458
+ let score = 0;
1459
+ const docLength = this.docLengths[index];
1460
+ for (const term of query) {
1461
+ const tf = doc.filter((t) => t === term).length;
1462
+ const idf = this.idf.get(term) || 0;
1463
+ score += idf * tf * (this.k1 + 1) / (tf + this.k1 * (1 - this.b + this.b * docLength / this.avgDocLength));
1464
+ }
1465
+ return score;
1466
+ }
1467
+ search(query) {
1468
+ const scores = this.documents.map((doc, idx) => ({
1469
+ doc,
1470
+ score: this.score(query, doc, idx)
1471
+ }));
1472
+ return scores.sort((a, b) => b.score - a.score).map((item) => item.doc);
1473
+ }
1474
+ };
1475
+
1476
+ // src/oss/src/graphs/tools.ts
1477
+ var RELATIONS_TOOL = {
1478
+ type: "function",
1479
+ function: {
1480
+ name: "establish_relationships",
1481
+ description: "Establish relationships among the entities based on the provided text.",
1482
+ parameters: {
1483
+ type: "object",
1484
+ properties: {
1485
+ entities: {
1486
+ type: "array",
1487
+ items: {
1488
+ type: "object",
1489
+ properties: {
1490
+ source: {
1491
+ type: "string",
1492
+ description: "The source entity of the relationship."
1493
+ },
1494
+ relationship: {
1495
+ type: "string",
1496
+ description: "The relationship between the source and destination entities."
1497
+ },
1498
+ destination: {
1499
+ type: "string",
1500
+ description: "The destination entity of the relationship."
1501
+ }
1502
+ },
1503
+ required: ["source", "relationship", "destination"],
1504
+ additionalProperties: false
1505
+ }
1506
+ }
1507
+ },
1508
+ required: ["entities"],
1509
+ additionalProperties: false
1510
+ }
1511
+ }
1512
+ };
1513
+ var EXTRACT_ENTITIES_TOOL = {
1514
+ type: "function",
1515
+ function: {
1516
+ name: "extract_entities",
1517
+ description: "Extract entities and their types from the text.",
1518
+ parameters: {
1519
+ type: "object",
1520
+ properties: {
1521
+ entities: {
1522
+ type: "array",
1523
+ items: {
1524
+ type: "object",
1525
+ properties: {
1526
+ entity: {
1527
+ type: "string",
1528
+ description: "The name or identifier of the entity."
1529
+ },
1530
+ entity_type: {
1531
+ type: "string",
1532
+ description: "The type or category of the entity."
1533
+ }
1534
+ },
1535
+ required: ["entity", "entity_type"],
1536
+ additionalProperties: false
1537
+ },
1538
+ description: "An array of entities with their types."
1539
+ }
1540
+ },
1541
+ required: ["entities"],
1542
+ additionalProperties: false
1543
+ }
1544
+ }
1545
+ };
1546
+ var DELETE_MEMORY_TOOL_GRAPH = {
1547
+ type: "function",
1548
+ function: {
1549
+ name: "delete_graph_memory",
1550
+ description: "Delete the relationship between two nodes.",
1551
+ parameters: {
1552
+ type: "object",
1553
+ properties: {
1554
+ source: {
1555
+ type: "string",
1556
+ description: "The identifier of the source node in the relationship."
1557
+ },
1558
+ relationship: {
1559
+ type: "string",
1560
+ description: "The existing relationship between the source and destination nodes that needs to be deleted."
1561
+ },
1562
+ destination: {
1563
+ type: "string",
1564
+ description: "The identifier of the destination node in the relationship."
1565
+ }
1566
+ },
1567
+ required: ["source", "relationship", "destination"],
1568
+ additionalProperties: false
1569
+ }
1570
+ }
1571
+ };
1572
+
1573
+ // src/oss/src/graphs/utils.ts
1574
+ var EXTRACT_RELATIONS_PROMPT = `
1575
+ You are an advanced algorithm designed to extract structured information from text to construct knowledge graphs. Your goal is to capture comprehensive and accurate information. Follow these key principles:
1576
+
1577
+ 1. Extract only explicitly stated information from the text.
1578
+ 2. Establish relationships among the entities provided.
1579
+ 3. Use "USER_ID" as the source entity for any self-references (e.g., "I," "me," "my," etc.) in user messages.
1580
+ CUSTOM_PROMPT
1581
+
1582
+ Relationships:
1583
+ - Use consistent, general, and timeless relationship types.
1584
+ - Example: Prefer "professor" over "became_professor."
1585
+ - Relationships should only be established among the entities explicitly mentioned in the user message.
1586
+
1587
+ Entity Consistency:
1588
+ - Ensure that relationships are coherent and logically align with the context of the message.
1589
+ - Maintain consistent naming for entities across the extracted data.
1590
+
1591
+ Strive to construct a coherent and easily understandable knowledge graph by eshtablishing all the relationships among the entities and adherence to the user's context.
1592
+
1593
+ Adhere strictly to these guidelines to ensure high-quality knowledge graph extraction.
1594
+ `;
1595
+ var DELETE_RELATIONS_SYSTEM_PROMPT = `
1596
+ You are a graph memory manager specializing in identifying, managing, and optimizing relationships within graph-based memories. Your primary task is to analyze a list of existing relationships and determine which ones should be deleted based on the new information provided.
1597
+ Input:
1598
+ 1. Existing Graph Memories: A list of current graph memories, each containing source, relationship, and destination information.
1599
+ 2. New Text: The new information to be integrated into the existing graph structure.
1600
+ 3. Use "USER_ID" as node for any self-references (e.g., "I," "me," "my," etc.) in user messages.
1601
+
1602
+ Guidelines:
1603
+ 1. Identification: Use the new information to evaluate existing relationships in the memory graph.
1604
+ 2. Deletion Criteria: Delete a relationship only if it meets at least one of these conditions:
1605
+ - Outdated or Inaccurate: The new information is more recent or accurate.
1606
+ - Contradictory: The new information conflicts with or negates the existing information.
1607
+ 3. DO NOT DELETE if their is a possibility of same type of relationship but different destination nodes.
1608
+ 4. Comprehensive Analysis:
1609
+ - Thoroughly examine each existing relationship against the new information and delete as necessary.
1610
+ - Multiple deletions may be required based on the new information.
1611
+ 5. Semantic Integrity:
1612
+ - Ensure that deletions maintain or improve the overall semantic structure of the graph.
1613
+ - Avoid deleting relationships that are NOT contradictory/outdated to the new information.
1614
+ 6. Temporal Awareness: Prioritize recency when timestamps are available.
1615
+ 7. Necessity Principle: Only DELETE relationships that must be deleted and are contradictory/outdated to the new information to maintain an accurate and coherent memory graph.
1616
+
1617
+ Note: DO NOT DELETE if their is a possibility of same type of relationship but different destination nodes.
1618
+
1619
+ For example:
1620
+ Existing Memory: alice -- loves_to_eat -- pizza
1621
+ New Information: Alice also loves to eat burger.
1622
+
1623
+ Do not delete in the above example because there is a possibility that Alice loves to eat both pizza and burger.
1624
+
1625
+ Memory Format:
1626
+ source -- relationship -- destination
1627
+
1628
+ Provide a list of deletion instructions, each specifying the relationship to be deleted.
1629
+ `;
1630
+ function getDeleteMessages(existingMemoriesString, data, userId) {
1631
+ return [
1632
+ DELETE_RELATIONS_SYSTEM_PROMPT.replace("USER_ID", userId),
1633
+ `Here are the existing memories: ${existingMemoriesString}
1634
+
1635
+ New Information: ${data}`
1636
+ ];
1637
+ }
1638
+
1639
+ // src/oss/src/utils/logger.ts
1640
+ var logger = {
1641
+ info: (message) => console.log(`[INFO] ${message}`),
1642
+ error: (message) => console.error(`[ERROR] ${message}`),
1643
+ debug: (message) => console.debug(`[DEBUG] ${message}`),
1644
+ warn: (message) => console.warn(`[WARN] ${message}`)
1645
+ };
1646
+
1647
+ // src/oss/src/memory/graph_memory.ts
1648
+ var MemoryGraph = class {
1649
+ constructor(config) {
1650
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1651
+ this.config = config;
1652
+ if (!((_b = (_a = config.graphStore) == null ? void 0 : _a.config) == null ? void 0 : _b.url) || !((_d = (_c = config.graphStore) == null ? void 0 : _c.config) == null ? void 0 : _d.username) || !((_f = (_e = config.graphStore) == null ? void 0 : _e.config) == null ? void 0 : _f.password)) {
1653
+ throw new Error("Neo4j configuration is incomplete");
1654
+ }
1655
+ this.graph = neo4j.driver(
1656
+ config.graphStore.config.url,
1657
+ neo4j.auth.basic(
1658
+ config.graphStore.config.username,
1659
+ config.graphStore.config.password
1660
+ )
1661
+ );
1662
+ this.embeddingModel = EmbedderFactory.create(
1663
+ this.config.embedder.provider,
1664
+ this.config.embedder.config
1665
+ );
1666
+ this.llmProvider = "openai";
1667
+ if ((_g = this.config.llm) == null ? void 0 : _g.provider) {
1668
+ this.llmProvider = this.config.llm.provider;
1669
+ }
1670
+ if ((_i = (_h = this.config.graphStore) == null ? void 0 : _h.llm) == null ? void 0 : _i.provider) {
1671
+ this.llmProvider = this.config.graphStore.llm.provider;
1672
+ }
1673
+ this.llm = LLMFactory.create(this.llmProvider, this.config.llm.config);
1674
+ this.structuredLlm = LLMFactory.create(
1675
+ "openai_structured",
1676
+ this.config.llm.config
1677
+ );
1678
+ this.threshold = 0.7;
1679
+ }
1680
+ async add(data, filters) {
1681
+ const entityTypeMap = await this._retrieveNodesFromData(data, filters);
1682
+ const toBeAdded = await this._establishNodesRelationsFromData(
1683
+ data,
1684
+ filters,
1685
+ entityTypeMap
1686
+ );
1687
+ const searchOutput = await this._searchGraphDb(
1688
+ Object.keys(entityTypeMap),
1689
+ filters
1690
+ );
1691
+ const toBeDeleted = await this._getDeleteEntitiesFromSearchOutput(
1692
+ searchOutput,
1693
+ data,
1694
+ filters
1695
+ );
1696
+ const deletedEntities = await this._deleteEntities(
1697
+ toBeDeleted,
1698
+ filters["userId"]
1699
+ );
1700
+ const addedEntities = await this._addEntities(
1701
+ toBeAdded,
1702
+ filters["userId"],
1703
+ entityTypeMap
1704
+ );
1705
+ return {
1706
+ deleted_entities: deletedEntities,
1707
+ added_entities: addedEntities,
1708
+ relations: toBeAdded
1709
+ };
1710
+ }
1711
+ async search(query, filters, limit = 100) {
1712
+ const entityTypeMap = await this._retrieveNodesFromData(query, filters);
1713
+ const searchOutput = await this._searchGraphDb(
1714
+ Object.keys(entityTypeMap),
1715
+ filters
1716
+ );
1717
+ if (!searchOutput.length) {
1718
+ return [];
1719
+ }
1720
+ const searchOutputsSequence = searchOutput.map((item) => [
1721
+ item.source,
1722
+ item.relationship,
1723
+ item.destination
1724
+ ]);
1725
+ const bm25 = new BM25(searchOutputsSequence);
1726
+ const tokenizedQuery = query.split(" ");
1727
+ const rerankedResults = bm25.search(tokenizedQuery).slice(0, 5);
1728
+ const searchResults = rerankedResults.map((item) => ({
1729
+ source: item[0],
1730
+ relationship: item[1],
1731
+ destination: item[2]
1732
+ }));
1733
+ logger.info(`Returned ${searchResults.length} search results`);
1734
+ return searchResults;
1735
+ }
1736
+ async deleteAll(filters) {
1737
+ const session = this.graph.session();
1738
+ try {
1739
+ await session.run("MATCH (n {user_id: $user_id}) DETACH DELETE n", {
1740
+ user_id: filters["userId"]
1741
+ });
1742
+ } finally {
1743
+ await session.close();
1744
+ }
1745
+ }
1746
+ async getAll(filters, limit = 100) {
1747
+ const session = this.graph.session();
1748
+ try {
1749
+ const result = await session.run(
1750
+ `
1751
+ MATCH (n {user_id: $user_id})-[r]->(m {user_id: $user_id})
1752
+ RETURN n.name AS source, type(r) AS relationship, m.name AS target
1753
+ LIMIT toInteger($limit)
1754
+ `,
1755
+ { user_id: filters["userId"], limit: Math.floor(Number(limit)) }
1756
+ );
1757
+ const finalResults = result.records.map((record) => ({
1758
+ source: record.get("source"),
1759
+ relationship: record.get("relationship"),
1760
+ target: record.get("target")
1761
+ }));
1762
+ logger.info(`Retrieved ${finalResults.length} relationships`);
1763
+ return finalResults;
1764
+ } finally {
1765
+ await session.close();
1766
+ }
1767
+ }
1768
+ async _retrieveNodesFromData(data, filters) {
1769
+ const tools = [EXTRACT_ENTITIES_TOOL];
1770
+ const searchResults = await this.structuredLlm.generateResponse(
1771
+ [
1772
+ {
1773
+ role: "system",
1774
+ content: `You are a smart assistant who understands entities and their types in a given text. If user message contains self reference such as 'I', 'me', 'my' etc. then use ${filters["userId"]} as the source entity. Extract all the entities from the text. ***DO NOT*** answer the question itself if the given text is a question.`
1775
+ },
1776
+ { role: "user", content: data }
1777
+ ],
1778
+ { type: "json_object" },
1779
+ tools
1780
+ );
1781
+ let entityTypeMap = {};
1782
+ try {
1783
+ if (typeof searchResults !== "string" && searchResults.toolCalls) {
1784
+ for (const call of searchResults.toolCalls) {
1785
+ if (call.name === "extract_entities") {
1786
+ const args = JSON.parse(call.arguments);
1787
+ for (const item of args.entities) {
1788
+ entityTypeMap[item.entity] = item.entity_type;
1789
+ }
1790
+ }
1791
+ }
1792
+ }
1793
+ } catch (e) {
1794
+ logger.error(`Error in search tool: ${e}`);
1795
+ }
1796
+ entityTypeMap = Object.fromEntries(
1797
+ Object.entries(entityTypeMap).map(([k, v]) => [
1798
+ k.toLowerCase().replace(/ /g, "_"),
1799
+ v.toLowerCase().replace(/ /g, "_")
1800
+ ])
1801
+ );
1802
+ logger.debug(`Entity type map: ${JSON.stringify(entityTypeMap)}`);
1803
+ return entityTypeMap;
1804
+ }
1805
+ async _establishNodesRelationsFromData(data, filters, entityTypeMap) {
1806
+ var _a;
1807
+ let messages;
1808
+ if ((_a = this.config.graphStore) == null ? void 0 : _a.customPrompt) {
1809
+ messages = [
1810
+ {
1811
+ role: "system",
1812
+ content: EXTRACT_RELATIONS_PROMPT.replace(
1813
+ "USER_ID",
1814
+ filters["userId"]
1815
+ ).replace(
1816
+ "CUSTOM_PROMPT",
1817
+ `4. ${this.config.graphStore.customPrompt}`
1818
+ ) + "\nPlease provide your response in JSON format."
1819
+ },
1820
+ { role: "user", content: data }
1821
+ ];
1822
+ } else {
1823
+ messages = [
1824
+ {
1825
+ role: "system",
1826
+ content: EXTRACT_RELATIONS_PROMPT.replace("USER_ID", filters["userId"]) + "\nPlease provide your response in JSON format."
1827
+ },
1828
+ {
1829
+ role: "user",
1830
+ content: `List of entities: ${Object.keys(entityTypeMap)}.
1831
+
1832
+ Text: ${data}`
1833
+ }
1834
+ ];
1835
+ }
1836
+ const tools = [RELATIONS_TOOL];
1837
+ const extractedEntities = await this.structuredLlm.generateResponse(
1838
+ messages,
1839
+ { type: "json_object" },
1840
+ tools
1841
+ );
1842
+ let entities = [];
1843
+ if (typeof extractedEntities !== "string" && extractedEntities.toolCalls) {
1844
+ const toolCall = extractedEntities.toolCalls[0];
1845
+ if (toolCall && toolCall.arguments) {
1846
+ const args = JSON.parse(toolCall.arguments);
1847
+ entities = args.entities || [];
1848
+ }
1849
+ }
1850
+ entities = this._removeSpacesFromEntities(entities);
1851
+ logger.debug(`Extracted entities: ${JSON.stringify(entities)}`);
1852
+ return entities;
1853
+ }
1854
+ async _searchGraphDb(nodeList, filters, limit = 100) {
1855
+ const resultRelations = [];
1856
+ const session = this.graph.session();
1857
+ try {
1858
+ for (const node of nodeList) {
1859
+ const nEmbedding = await this.embeddingModel.embed(node);
1860
+ const cypher = `
1861
+ MATCH (n)
1862
+ WHERE n.embedding IS NOT NULL AND n.user_id = $user_id
1863
+ WITH n,
1864
+ round(reduce(dot = 0.0, i IN range(0, size(n.embedding)-1) | dot + n.embedding[i] * $n_embedding[i]) /
1865
+ (sqrt(reduce(l2 = 0.0, i IN range(0, size(n.embedding)-1) | l2 + n.embedding[i] * n.embedding[i])) *
1866
+ sqrt(reduce(l2 = 0.0, i IN range(0, size($n_embedding)-1) | l2 + $n_embedding[i] * $n_embedding[i]))), 4) AS similarity
1867
+ WHERE similarity >= $threshold
1868
+ MATCH (n)-[r]->(m)
1869
+ RETURN n.name AS source, elementId(n) AS source_id, type(r) AS relationship, elementId(r) AS relation_id, m.name AS destination, elementId(m) AS destination_id, similarity
1870
+ UNION
1871
+ MATCH (n)
1872
+ WHERE n.embedding IS NOT NULL AND n.user_id = $user_id
1873
+ WITH n,
1874
+ round(reduce(dot = 0.0, i IN range(0, size(n.embedding)-1) | dot + n.embedding[i] * $n_embedding[i]) /
1875
+ (sqrt(reduce(l2 = 0.0, i IN range(0, size(n.embedding)-1) | l2 + n.embedding[i] * n.embedding[i])) *
1876
+ sqrt(reduce(l2 = 0.0, i IN range(0, size($n_embedding)-1) | l2 + $n_embedding[i] * $n_embedding[i]))), 4) AS similarity
1877
+ WHERE similarity >= $threshold
1878
+ MATCH (m)-[r]->(n)
1879
+ RETURN m.name AS source, elementId(m) AS source_id, type(r) AS relationship, elementId(r) AS relation_id, n.name AS destination, elementId(n) AS destination_id, similarity
1880
+ ORDER BY similarity DESC
1881
+ LIMIT toInteger($limit)
1882
+ `;
1883
+ const result = await session.run(cypher, {
1884
+ n_embedding: nEmbedding,
1885
+ threshold: this.threshold,
1886
+ user_id: filters["userId"],
1887
+ limit: Math.floor(Number(limit))
1888
+ });
1889
+ resultRelations.push(
1890
+ ...result.records.map((record) => ({
1891
+ source: record.get("source"),
1892
+ source_id: record.get("source_id").toString(),
1893
+ relationship: record.get("relationship"),
1894
+ relation_id: record.get("relation_id").toString(),
1895
+ destination: record.get("destination"),
1896
+ destination_id: record.get("destination_id").toString(),
1897
+ similarity: record.get("similarity")
1898
+ }))
1899
+ );
1900
+ }
1901
+ } finally {
1902
+ await session.close();
1903
+ }
1904
+ return resultRelations;
1905
+ }
1906
+ async _getDeleteEntitiesFromSearchOutput(searchOutput, data, filters) {
1907
+ const searchOutputString = searchOutput.map(
1908
+ (item) => `${item.source} -- ${item.relationship} -- ${item.destination}`
1909
+ ).join("\n");
1910
+ const [systemPrompt, userPrompt] = getDeleteMessages(
1911
+ searchOutputString,
1912
+ data,
1913
+ filters["userId"]
1914
+ );
1915
+ const tools = [DELETE_MEMORY_TOOL_GRAPH];
1916
+ const memoryUpdates = await this.structuredLlm.generateResponse(
1917
+ [
1918
+ { role: "system", content: systemPrompt },
1919
+ { role: "user", content: userPrompt }
1920
+ ],
1921
+ { type: "json_object" },
1922
+ tools
1923
+ );
1924
+ const toBeDeleted = [];
1925
+ if (typeof memoryUpdates !== "string" && memoryUpdates.toolCalls) {
1926
+ for (const item of memoryUpdates.toolCalls) {
1927
+ if (item.name === "delete_graph_memory") {
1928
+ toBeDeleted.push(JSON.parse(item.arguments));
1929
+ }
1930
+ }
1931
+ }
1932
+ const cleanedToBeDeleted = this._removeSpacesFromEntities(toBeDeleted);
1933
+ logger.debug(
1934
+ `Deleted relationships: ${JSON.stringify(cleanedToBeDeleted)}`
1935
+ );
1936
+ return cleanedToBeDeleted;
1937
+ }
1938
+ async _deleteEntities(toBeDeleted, userId) {
1939
+ const results = [];
1940
+ const session = this.graph.session();
1941
+ try {
1942
+ for (const item of toBeDeleted) {
1943
+ const { source, destination, relationship } = item;
1944
+ const cypher = `
1945
+ MATCH (n {name: $source_name, user_id: $user_id})
1946
+ -[r:${relationship}]->
1947
+ (m {name: $dest_name, user_id: $user_id})
1948
+ DELETE r
1949
+ RETURN
1950
+ n.name AS source,
1951
+ m.name AS target,
1952
+ type(r) AS relationship
1953
+ `;
1954
+ const result = await session.run(cypher, {
1955
+ source_name: source,
1956
+ dest_name: destination,
1957
+ user_id: userId
1958
+ });
1959
+ results.push(result.records);
1960
+ }
1961
+ } finally {
1962
+ await session.close();
1963
+ }
1964
+ return results;
1965
+ }
1966
+ async _addEntities(toBeAdded, userId, entityTypeMap) {
1967
+ var _a, _b;
1968
+ const results = [];
1969
+ const session = this.graph.session();
1970
+ try {
1971
+ for (const item of toBeAdded) {
1972
+ const { source, destination, relationship } = item;
1973
+ const sourceType = entityTypeMap[source] || "unknown";
1974
+ const destinationType = entityTypeMap[destination] || "unknown";
1975
+ const sourceEmbedding = await this.embeddingModel.embed(source);
1976
+ const destEmbedding = await this.embeddingModel.embed(destination);
1977
+ const sourceNodeSearchResult = await this._searchSourceNode(
1978
+ sourceEmbedding,
1979
+ userId
1980
+ );
1981
+ const destinationNodeSearchResult = await this._searchDestinationNode(
1982
+ destEmbedding,
1983
+ userId
1984
+ );
1985
+ let cypher;
1986
+ let params;
1987
+ if (destinationNodeSearchResult.length === 0 && sourceNodeSearchResult.length > 0) {
1988
+ cypher = `
1989
+ MATCH (source)
1990
+ WHERE elementId(source) = $source_id
1991
+ MERGE (destination:${destinationType} {name: $destination_name, user_id: $user_id})
1992
+ ON CREATE SET
1993
+ destination.created = timestamp(),
1994
+ destination.embedding = $destination_embedding
1995
+ MERGE (source)-[r:${relationship}]->(destination)
1996
+ ON CREATE SET
1997
+ r.created = timestamp()
1998
+ RETURN source.name AS source, type(r) AS relationship, destination.name AS target
1999
+ `;
2000
+ params = {
2001
+ source_id: sourceNodeSearchResult[0].elementId,
2002
+ destination_name: destination,
2003
+ destination_embedding: destEmbedding,
2004
+ user_id: userId
2005
+ };
2006
+ } else if (destinationNodeSearchResult.length > 0 && sourceNodeSearchResult.length === 0) {
2007
+ cypher = `
2008
+ MATCH (destination)
2009
+ WHERE elementId(destination) = $destination_id
2010
+ MERGE (source:${sourceType} {name: $source_name, user_id: $user_id})
2011
+ ON CREATE SET
2012
+ source.created = timestamp(),
2013
+ source.embedding = $source_embedding
2014
+ MERGE (source)-[r:${relationship}]->(destination)
2015
+ ON CREATE SET
2016
+ r.created = timestamp()
2017
+ RETURN source.name AS source, type(r) AS relationship, destination.name AS target
2018
+ `;
2019
+ params = {
2020
+ destination_id: destinationNodeSearchResult[0].elementId,
2021
+ source_name: source,
2022
+ source_embedding: sourceEmbedding,
2023
+ user_id: userId
2024
+ };
2025
+ } else if (sourceNodeSearchResult.length > 0 && destinationNodeSearchResult.length > 0) {
2026
+ cypher = `
2027
+ MATCH (source)
2028
+ WHERE elementId(source) = $source_id
2029
+ MATCH (destination)
2030
+ WHERE elementId(destination) = $destination_id
2031
+ MERGE (source)-[r:${relationship}]->(destination)
2032
+ ON CREATE SET
2033
+ r.created_at = timestamp(),
2034
+ r.updated_at = timestamp()
2035
+ RETURN source.name AS source, type(r) AS relationship, destination.name AS target
2036
+ `;
2037
+ params = {
2038
+ source_id: (_a = sourceNodeSearchResult[0]) == null ? void 0 : _a.elementId,
2039
+ destination_id: (_b = destinationNodeSearchResult[0]) == null ? void 0 : _b.elementId,
2040
+ user_id: userId
2041
+ };
2042
+ } else {
2043
+ cypher = `
2044
+ MERGE (n:${sourceType} {name: $source_name, user_id: $user_id})
2045
+ ON CREATE SET n.created = timestamp(), n.embedding = $source_embedding
2046
+ ON MATCH SET n.embedding = $source_embedding
2047
+ MERGE (m:${destinationType} {name: $dest_name, user_id: $user_id})
2048
+ ON CREATE SET m.created = timestamp(), m.embedding = $dest_embedding
2049
+ ON MATCH SET m.embedding = $dest_embedding
2050
+ MERGE (n)-[rel:${relationship}]->(m)
2051
+ ON CREATE SET rel.created = timestamp()
2052
+ RETURN n.name AS source, type(rel) AS relationship, m.name AS target
2053
+ `;
2054
+ params = {
2055
+ source_name: source,
2056
+ dest_name: destination,
2057
+ source_embedding: sourceEmbedding,
2058
+ dest_embedding: destEmbedding,
2059
+ user_id: userId
2060
+ };
2061
+ }
2062
+ const result = await session.run(cypher, params);
2063
+ results.push(result.records);
2064
+ }
2065
+ } finally {
2066
+ await session.close();
2067
+ }
2068
+ return results;
2069
+ }
2070
+ _removeSpacesFromEntities(entityList) {
2071
+ return entityList.map((item) => ({
2072
+ ...item,
2073
+ source: item.source.toLowerCase().replace(/ /g, "_"),
2074
+ relationship: item.relationship.toLowerCase().replace(/ /g, "_"),
2075
+ destination: item.destination.toLowerCase().replace(/ /g, "_")
2076
+ }));
2077
+ }
2078
+ async _searchSourceNode(sourceEmbedding, userId, threshold = 0.9) {
2079
+ const session = this.graph.session();
2080
+ try {
2081
+ const cypher = `
2082
+ MATCH (source_candidate)
2083
+ WHERE source_candidate.embedding IS NOT NULL
2084
+ AND source_candidate.user_id = $user_id
2085
+
2086
+ WITH source_candidate,
2087
+ round(
2088
+ reduce(dot = 0.0, i IN range(0, size(source_candidate.embedding)-1) |
2089
+ dot + source_candidate.embedding[i] * $source_embedding[i]) /
2090
+ (sqrt(reduce(l2 = 0.0, i IN range(0, size(source_candidate.embedding)-1) |
2091
+ l2 + source_candidate.embedding[i] * source_candidate.embedding[i])) *
2092
+ sqrt(reduce(l2 = 0.0, i IN range(0, size($source_embedding)-1) |
2093
+ l2 + $source_embedding[i] * $source_embedding[i])))
2094
+ , 4) AS source_similarity
2095
+ WHERE source_similarity >= $threshold
2096
+
2097
+ WITH source_candidate, source_similarity
2098
+ ORDER BY source_similarity DESC
2099
+ LIMIT 1
2100
+
2101
+ RETURN elementId(source_candidate) as element_id
2102
+ `;
2103
+ const params = {
2104
+ source_embedding: sourceEmbedding,
2105
+ user_id: userId,
2106
+ threshold
2107
+ };
2108
+ const result = await session.run(cypher, params);
2109
+ return result.records.map((record) => ({
2110
+ elementId: record.get("element_id").toString()
2111
+ }));
2112
+ } finally {
2113
+ await session.close();
2114
+ }
2115
+ }
2116
+ async _searchDestinationNode(destinationEmbedding, userId, threshold = 0.9) {
2117
+ const session = this.graph.session();
2118
+ try {
2119
+ const cypher = `
2120
+ MATCH (destination_candidate)
2121
+ WHERE destination_candidate.embedding IS NOT NULL
2122
+ AND destination_candidate.user_id = $user_id
2123
+
2124
+ WITH destination_candidate,
2125
+ round(
2126
+ reduce(dot = 0.0, i IN range(0, size(destination_candidate.embedding)-1) |
2127
+ dot + destination_candidate.embedding[i] * $destination_embedding[i]) /
2128
+ (sqrt(reduce(l2 = 0.0, i IN range(0, size(destination_candidate.embedding)-1) |
2129
+ l2 + destination_candidate.embedding[i] * destination_candidate.embedding[i])) *
2130
+ sqrt(reduce(l2 = 0.0, i IN range(0, size($destination_embedding)-1) |
2131
+ l2 + $destination_embedding[i] * $destination_embedding[i])))
2132
+ , 4) AS destination_similarity
2133
+ WHERE destination_similarity >= $threshold
2134
+
2135
+ WITH destination_candidate, destination_similarity
2136
+ ORDER BY destination_similarity DESC
2137
+ LIMIT 1
2138
+
2139
+ RETURN elementId(destination_candidate) as element_id
2140
+ `;
2141
+ const params = {
2142
+ destination_embedding: destinationEmbedding,
2143
+ user_id: userId,
2144
+ threshold
2145
+ };
2146
+ const result = await session.run(cypher, params);
2147
+ return result.records.map((record) => ({
2148
+ elementId: record.get("element_id").toString()
2149
+ }));
2150
+ } finally {
2151
+ await session.close();
2152
+ }
2153
+ }
2154
+ };
2155
+
2156
+ // src/oss/src/utils/memory.ts
2157
+ var get_image_description = async (image_url) => {
2158
+ const llm = new OpenAILLM({
2159
+ apiKey: process.env.OPENAI_API_KEY
2160
+ });
2161
+ const response = await llm.generateResponse([
2162
+ {
2163
+ role: "user",
2164
+ content: "Provide a description of the image and do not include any additional text."
2165
+ },
2166
+ {
2167
+ role: "user",
2168
+ content: { type: "image_url", image_url: { url: image_url } }
2169
+ }
2170
+ ]);
2171
+ return response;
2172
+ };
2173
+ var parse_vision_messages = async (messages) => {
2174
+ const parsed_messages = [];
2175
+ for (const message of messages) {
2176
+ let new_message = {
2177
+ role: message.role,
2178
+ content: ""
2179
+ };
2180
+ if (message.role !== "system") {
2181
+ if (typeof message.content === "object" && message.content.type === "image_url") {
2182
+ const description = await get_image_description(
2183
+ message.content.image_url.url
2184
+ );
2185
+ new_message.content = typeof description === "string" ? description : JSON.stringify(description);
2186
+ parsed_messages.push(new_message);
2187
+ } else parsed_messages.push(message);
2188
+ }
2189
+ }
2190
+ return parsed_messages;
2191
+ };
2192
+
1362
2193
  // src/oss/src/memory/index.ts
1363
2194
  var Memory = class _Memory {
1364
2195
  constructor(config = {}) {
@@ -1379,6 +2210,10 @@ var Memory = class _Memory {
1379
2210
  this.db = new SQLiteManager(this.config.historyDbPath || ":memory:");
1380
2211
  this.collectionName = this.config.vectorStore.config.collectionName;
1381
2212
  this.apiVersion = this.config.version || "v1.0";
2213
+ this.enableGraph = this.config.enableGraph || false;
2214
+ if (this.enableGraph && this.config.graphStore) {
2215
+ this.graphMemory = new MemoryGraph(this.config);
2216
+ }
1382
2217
  }
1383
2218
  static fromConfig(configDict) {
1384
2219
  try {
@@ -1407,12 +2242,27 @@ var Memory = class _Memory {
1407
2242
  );
1408
2243
  }
1409
2244
  const parsedMessages = Array.isArray(messages) ? messages : [{ role: "user", content: messages }];
2245
+ const final_parsedMessages = await parse_vision_messages(parsedMessages);
1410
2246
  const vectorStoreResult = await this.addToVectorStore(
1411
- parsedMessages,
2247
+ final_parsedMessages,
1412
2248
  metadata,
1413
2249
  filters
1414
2250
  );
1415
- return { results: vectorStoreResult };
2251
+ let graphResult;
2252
+ if (this.graphMemory) {
2253
+ try {
2254
+ graphResult = await this.graphMemory.add(
2255
+ final_parsedMessages.map((m) => m.content).join("\n"),
2256
+ filters
2257
+ );
2258
+ } catch (error) {
2259
+ console.error("Error adding to graph memory:", error);
2260
+ }
2261
+ }
2262
+ return {
2263
+ results: vectorStoreResult,
2264
+ relations: graphResult == null ? void 0 : graphResult.relations
2265
+ };
1416
2266
  }
1417
2267
  async addToVectorStore(messages, metadata, filters) {
1418
2268
  const parsedMessages = messages.map((m) => m.content).join("\n");
@@ -1556,6 +2406,14 @@ ${parsedMessages}`] : getFactRetrievalMessages(parsedMessages);
1556
2406
  limit,
1557
2407
  filters
1558
2408
  );
2409
+ let graphResults;
2410
+ if (this.graphMemory) {
2411
+ try {
2412
+ graphResults = await this.graphMemory.search(query, filters);
2413
+ } catch (error) {
2414
+ console.error("Error searching graph memory:", error);
2415
+ }
2416
+ }
1559
2417
  const excludedKeys = /* @__PURE__ */ new Set([
1560
2418
  "userId",
1561
2419
  "agentId",
@@ -1577,7 +2435,10 @@ ${parsedMessages}`] : getFactRetrievalMessages(parsedMessages);
1577
2435
  ...mem.payload.agentId && { agentId: mem.payload.agentId },
1578
2436
  ...mem.payload.runId && { runId: mem.payload.runId }
1579
2437
  }));
1580
- return { results };
2438
+ return {
2439
+ results,
2440
+ relations: graphResults
2441
+ };
1581
2442
  }
1582
2443
  async update(memoryId, data) {
1583
2444
  const embedding = await this.embedder.embed(data);
@@ -1611,6 +2472,9 @@ ${parsedMessages}`] : getFactRetrievalMessages(parsedMessages);
1611
2472
  async reset() {
1612
2473
  await this.db.reset();
1613
2474
  await this.vectorStore.deleteCol();
2475
+ if (this.graphMemory) {
2476
+ await this.graphMemory.deleteAll({ userId: "default" });
2477
+ }
1614
2478
  this.vectorStore = VectorStoreFactory.create(
1615
2479
  this.config.vectorStore.provider,
1616
2480
  this.config.vectorStore.config