neo4j-agent-memory 0.4.0 → 0.5.0
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.
- package/README.md +94 -2
- package/dist/cypher/auto_relate_memory_by_tags.cypher +9 -0
- package/dist/cypher/fallback_retrieve_memories.cypher +68 -0
- package/dist/cypher/feedback_batch.cypher +20 -26
- package/dist/cypher/get_memories_by_id.cypher +41 -0
- package/dist/cypher/get_memory_graph.cypher +108 -0
- package/dist/cypher/index.ts +4 -0
- package/dist/cypher/list_memory_edges.cypher +37 -0
- package/dist/index.cjs +189 -19
- package/dist/index.d.ts +96 -9
- package/dist/index.js +189 -19
- package/package.json +1 -1
- package/dist/index.d.cts +0 -246
package/README.md
CHANGED
|
@@ -52,10 +52,16 @@ const bundle = await mem.retrieveContextBundle({
|
|
|
52
52
|
prompt: "EACCES cannot create node_modules",
|
|
53
53
|
symptoms: ["eacces", "permission denied", "node_modules"],
|
|
54
54
|
tags: ["npm", "node_modules"],
|
|
55
|
-
env: { os: "macos", packageManager: "npm", container: false }
|
|
55
|
+
env: { os: "macos", packageManager: "npm", container: false },
|
|
56
|
+
fallback: {
|
|
57
|
+
enabled: true,
|
|
58
|
+
useFulltext: true,
|
|
59
|
+
useTags: true,
|
|
60
|
+
useVector: false,
|
|
61
|
+
},
|
|
56
62
|
});
|
|
57
63
|
|
|
58
|
-
await mem.feedback({
|
|
64
|
+
const feedback = await mem.feedback({
|
|
59
65
|
agentId: "auggie",
|
|
60
66
|
sessionId: bundle.sessionId,
|
|
61
67
|
usedIds: bundle.sections.fix.map((m) => m.id),
|
|
@@ -69,6 +75,9 @@ await mem.close();
|
|
|
69
75
|
Notes:
|
|
70
76
|
- `createMemoryService` runs schema setup on init.
|
|
71
77
|
- Cypher assets are bundled at `dist/cypher` in the published package.
|
|
78
|
+
- `feedback()` returns updated RECALLS edge posteriors for the provided memory ids.
|
|
79
|
+
- Neutral usage: pass `neutralIds` or `updateUnratedUsed: false` to avoid penalizing retrieved-but-unrated memories.
|
|
80
|
+
- Fallback retrieval uses fulltext/tag (and optional vector) search; provide `fallback.embedding` when using vector indexes.
|
|
72
81
|
|
|
73
82
|
Auto-relate config (defaults):
|
|
74
83
|
- `enabled: true`
|
|
@@ -89,6 +98,21 @@ Auto-relate behavior:
|
|
|
89
98
|
Performance note:
|
|
90
99
|
- Auto-relate scans candidate memories with tag filtering; for large graphs, keep tags selective and consider tightening `maxCandidates` and `minSharedTags`.
|
|
91
100
|
|
|
101
|
+
Neo4j Browser params (auto-relate by tags):
|
|
102
|
+
|
|
103
|
+
```cypher
|
|
104
|
+
:param nowIso => "2026-01-04T22:07:53.086Z";
|
|
105
|
+
:param id => "mem_8cd773c2-208c-45ad-97ea-1b2337dca751";
|
|
106
|
+
:param minSharedTags => 2;
|
|
107
|
+
:param minWeight => 0.3;
|
|
108
|
+
:param maxCandidates => 10;
|
|
109
|
+
:param sameKind => false;
|
|
110
|
+
:param samePolarity => false;
|
|
111
|
+
:param allowedKinds => [];
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Note: `:param` lines are only supported in Neo4j Browser; other runners should pass parameters via the driver.
|
|
115
|
+
|
|
92
116
|
## Tool adapter (createMemoryTools)
|
|
93
117
|
|
|
94
118
|
Use the tool factory to preserve the existing tool surface used by the demo:
|
|
@@ -127,6 +151,42 @@ const skills = await mem.listSkills({ agentId: "auggie" });
|
|
|
127
151
|
const concepts = await mem.listConcepts({ agentId: "auggie" });
|
|
128
152
|
```
|
|
129
153
|
|
|
154
|
+
## Graph APIs
|
|
155
|
+
|
|
156
|
+
Fetch full memory records by id:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const records = await mem.getMemoriesById({ ids: ["mem-1", "mem-2"] });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Retrieve a weighted subgraph for UI maps:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
const graph = await mem.getMemoryGraph({
|
|
166
|
+
agentId: "auggie",
|
|
167
|
+
memoryIds: ["mem-1", "mem-2"],
|
|
168
|
+
includeNodes: true,
|
|
169
|
+
includeRelatedTo: true,
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
List edges for analytics/audit:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
const edges = await mem.listMemoryEdges({ limit: 500, minStrength: 0.2 });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Retrieve a bundle with graph edges in one call:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const bundleWithGraph = await mem.retrieveContextBundleWithGraph({
|
|
183
|
+
agentId: "auggie",
|
|
184
|
+
prompt: "EACCES cannot create node_modules",
|
|
185
|
+
tags: ["npm", "node_modules"],
|
|
186
|
+
includeRelatedTo: true,
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
130
190
|
## Episodic capture helpers
|
|
131
191
|
|
|
132
192
|
```ts
|
|
@@ -151,6 +211,38 @@ await mem.captureStepEpisode({
|
|
|
151
211
|
});
|
|
152
212
|
```
|
|
153
213
|
|
|
214
|
+
## Useful learning capture
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
await mem.captureUsefulLearning({
|
|
218
|
+
agentId: "auggie",
|
|
219
|
+
sessionId: "run-123",
|
|
220
|
+
useful: true,
|
|
221
|
+
learning: {
|
|
222
|
+
kind: "semantic",
|
|
223
|
+
title: "Avoid chmod 777 on node_modules",
|
|
224
|
+
content: "Use npm cache ownership fixes instead of chmod 777.",
|
|
225
|
+
tags: ["npm", "permissions"],
|
|
226
|
+
confidence: 0.8,
|
|
227
|
+
utility: 0.3,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Case helpers
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
await mem.createCase({
|
|
236
|
+
title: "npm EACCES",
|
|
237
|
+
summary: "Permission denied on cache directory.",
|
|
238
|
+
outcome: "resolved",
|
|
239
|
+
symptoms: ["eacces", "permission denied"],
|
|
240
|
+
env: { os: "macos", packageManager: "npm" },
|
|
241
|
+
resolvedByMemoryIds: ["mem-1"],
|
|
242
|
+
negativeMemoryIds: [],
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
154
246
|
## Event hooks
|
|
155
247
|
|
|
156
248
|
Provide an `onMemoryEvent` callback to observe reads/writes:
|
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
// - $sameKind: Only relate to same kind if true
|
|
8
8
|
// - $samePolarity: Only relate to same polarity if true
|
|
9
9
|
// - $allowedKinds: Optional list of kinds to consider (empty = all)
|
|
10
|
+
// Neo4j Browser params (example only):
|
|
11
|
+
// :param nowIso => "2026-01-04T22:07:53.086Z";
|
|
12
|
+
// :param id => "mem_8cd773c2-208c-45ad-97ea-1b2337dca751";
|
|
13
|
+
// :param minSharedTags => 2;
|
|
14
|
+
// :param minWeight => 0.3;
|
|
15
|
+
// :param maxCandidates => 10;
|
|
16
|
+
// :param sameKind => false;
|
|
17
|
+
// :param samePolarity => false;
|
|
18
|
+
// :param allowedKinds => [];
|
|
10
19
|
WITH datetime($nowIso) AS now
|
|
11
20
|
MATCH (src:Memory {id: $id})
|
|
12
21
|
WITH src, now, coalesce(src.tags, []) AS srcTags
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Parameters:
|
|
2
|
+
// - $prompt: Query text
|
|
3
|
+
// - $tags: Array of tags
|
|
4
|
+
// - $kinds: Optional kinds filter
|
|
5
|
+
// - $fulltextIndex: Fulltext index name
|
|
6
|
+
// - $vectorIndex: Vector index name
|
|
7
|
+
// - $embedding: Optional embedding vector
|
|
8
|
+
// - $useFulltext: boolean
|
|
9
|
+
// - $useVector: boolean
|
|
10
|
+
// - $useTags: boolean
|
|
11
|
+
// - $fixLimit: number
|
|
12
|
+
// - $dontLimit: number
|
|
13
|
+
WITH
|
|
14
|
+
coalesce($prompt, "") AS prompt,
|
|
15
|
+
coalesce($tags, []) AS tags,
|
|
16
|
+
coalesce($kinds, []) AS kinds,
|
|
17
|
+
coalesce($fulltextIndex, "") AS fulltextIndex,
|
|
18
|
+
coalesce($vectorIndex, "") AS vectorIndex,
|
|
19
|
+
$embedding AS embedding,
|
|
20
|
+
coalesce($useFulltext, true) AS useFulltext,
|
|
21
|
+
coalesce($useVector, false) AS useVector,
|
|
22
|
+
coalesce($useTags, true) AS useTags,
|
|
23
|
+
coalesce($fixLimit, 8) AS fixLimit,
|
|
24
|
+
coalesce($dontLimit, 6) AS dontLimit
|
|
25
|
+
|
|
26
|
+
CALL {
|
|
27
|
+
WITH useFulltext, fulltextIndex, prompt
|
|
28
|
+
WHERE useFulltext = true AND fulltextIndex <> "" AND prompt <> ""
|
|
29
|
+
CALL db.index.fulltext.queryNodes(fulltextIndex, prompt) YIELD node, score
|
|
30
|
+
RETURN node AS m, score AS score
|
|
31
|
+
|
|
32
|
+
UNION
|
|
33
|
+
|
|
34
|
+
WITH useTags, tags
|
|
35
|
+
WHERE useTags = true AND size(tags) > 0
|
|
36
|
+
MATCH (m:Memory)
|
|
37
|
+
WHERE any(t IN tags WHERE t IN coalesce(m.tags, []))
|
|
38
|
+
RETURN m, 0.1 AS score
|
|
39
|
+
|
|
40
|
+
UNION
|
|
41
|
+
|
|
42
|
+
WITH useVector, vectorIndex, embedding
|
|
43
|
+
WHERE useVector = true AND vectorIndex <> "" AND embedding IS NOT NULL
|
|
44
|
+
CALL db.index.vector.queryNodes(vectorIndex, embedding, 20) YIELD node, score
|
|
45
|
+
RETURN node AS m, score AS score
|
|
46
|
+
}
|
|
47
|
+
WITH m, max(score) AS score, kinds, fixLimit, dontLimit
|
|
48
|
+
WHERE m IS NOT NULL AND (size(kinds) = 0 OR m.kind IN kinds)
|
|
49
|
+
WITH m, score, fixLimit, dontLimit
|
|
50
|
+
ORDER BY score DESC, m.updatedAt DESC
|
|
51
|
+
|
|
52
|
+
WITH collect(m {
|
|
53
|
+
.id,
|
|
54
|
+
.kind,
|
|
55
|
+
.polarity,
|
|
56
|
+
.title,
|
|
57
|
+
.content,
|
|
58
|
+
.tags,
|
|
59
|
+
.confidence,
|
|
60
|
+
.utility,
|
|
61
|
+
.updatedAt
|
|
62
|
+
}) AS rows, fixLimit, dontLimit
|
|
63
|
+
|
|
64
|
+
WITH
|
|
65
|
+
[m IN rows WHERE m.polarity <> "negative"][0..fixLimit] AS fixes,
|
|
66
|
+
[m IN rows WHERE m.polarity = "negative"][0..dontLimit] AS doNot
|
|
67
|
+
|
|
68
|
+
RETURN { fixes: fixes, doNot: doNot } AS sections;
|
|
@@ -4,30 +4,19 @@
|
|
|
4
4
|
// halfLifeSeconds: 86400,
|
|
5
5
|
// aMin: 1.0,
|
|
6
6
|
// bMin: 1.0,
|
|
7
|
-
// w: 1.0,
|
|
8
|
-
// y: 1.0,
|
|
9
7
|
// items: [
|
|
10
|
-
// "mem_8cd773c2-208c-45ad-97ea-1b2337dca751",
|
|
11
|
-
// "mem_64fbcc73-0b5c-4041-8b6b-66514ffaf1d0",
|
|
12
|
-
// "mem_e55b80e2-6156-47bc-a964-9aef596f74a6",
|
|
13
|
-
// "mem_13189119-e3ad-4769-9f2b-2d3e3b8bc07f",
|
|
14
|
-
// "mem_137c3ff7-a81d-453d-8048-5c1b736db6ca",
|
|
15
|
-
// "mem_c0ea5643-3c61-4607-ba9f-67f4c2bb01ee",
|
|
16
|
-
// "mem_aa15e7b6-7f94-445a-9c52-3eb24a315215",
|
|
17
|
-
// "mem_e93e7bb8-b43c-44f5-a5d3-87545d67be59",
|
|
18
|
-
// "mem_b81e3709-35ae-4cab-931d-612dcc2cd43d",
|
|
19
|
-
// "mem_ce8a5b62-7ddb-4618-8a6c-93ed3b425f27",
|
|
20
|
-
// "mem_737df25b-7944-4dee-a7aa-86af93567663"
|
|
8
|
+
// { memoryId: "mem_8cd773c2-208c-45ad-97ea-1b2337dca751", w: 1.0, y: 1.0 },
|
|
9
|
+
// { memoryId: "mem_64fbcc73-0b5c-4041-8b6b-66514ffaf1d0", w: 1.0, y: 0.0 }
|
|
21
10
|
// ]
|
|
22
11
|
// }
|
|
23
12
|
WITH datetime($nowIso) AS now
|
|
24
13
|
|
|
25
|
-
UNWIND $items AS
|
|
26
|
-
WITH now,
|
|
27
|
-
WHERE memoryId IS NOT NULL AND memoryId <> ""
|
|
14
|
+
UNWIND $items AS item
|
|
15
|
+
WITH now, item
|
|
16
|
+
WHERE item.memoryId IS NOT NULL AND item.memoryId <> ""
|
|
28
17
|
|
|
29
18
|
MATCH (a:Agent {id: $agentId})
|
|
30
|
-
MATCH (m:Memory {id: memoryId})
|
|
19
|
+
MATCH (m:Memory {id: item.memoryId})
|
|
31
20
|
|
|
32
21
|
MERGE (a)-[r:RECALLS]->(m)
|
|
33
22
|
ON CREATE SET
|
|
@@ -38,25 +27,25 @@ ON CREATE SET
|
|
|
38
27
|
r.successes = 0,
|
|
39
28
|
r.failures = 0
|
|
40
29
|
|
|
41
|
-
WITH now, r,
|
|
30
|
+
WITH now, r, item,
|
|
42
31
|
CASE
|
|
43
32
|
WHEN duration.inSeconds(coalesce(r.updatedAt, now), now).seconds > 0
|
|
44
33
|
THEN duration.inSeconds(coalesce(r.updatedAt, now), now).seconds
|
|
45
34
|
ELSE 0
|
|
46
35
|
END AS dt
|
|
47
36
|
|
|
48
|
-
WITH now, r,
|
|
37
|
+
WITH now, r, item,
|
|
49
38
|
0.5 ^ (dt / $halfLifeSeconds) AS gamma,
|
|
50
39
|
coalesce(r.a, $aMin) AS aPrev,
|
|
51
40
|
coalesce(r.b, $bMin) AS bPrev
|
|
52
41
|
|
|
53
|
-
WITH now, r, gamma,
|
|
42
|
+
WITH now, r, item, gamma,
|
|
54
43
|
($aMin + gamma * (aPrev - $aMin)) AS a0,
|
|
55
44
|
($bMin + gamma * (bPrev - $bMin)) AS b0
|
|
56
45
|
|
|
57
|
-
WITH now, r,
|
|
58
|
-
(a0 +
|
|
59
|
-
(b0 +
|
|
46
|
+
WITH now, r, item,
|
|
47
|
+
(a0 + item.w * item.y) AS a1,
|
|
48
|
+
(b0 + item.w * (1.0 - item.y)) AS b1
|
|
60
49
|
|
|
61
50
|
SET r.a = a1,
|
|
62
51
|
r.b = b1,
|
|
@@ -64,7 +53,12 @@ SET r.a = a1,
|
|
|
64
53
|
r.evidence = a1 + b1,
|
|
65
54
|
r.updatedAt = now,
|
|
66
55
|
r.uses = coalesce(r.uses, 0) + 1,
|
|
67
|
-
r.successes = coalesce(r.successes, 0) + CASE WHEN
|
|
68
|
-
r.failures = coalesce(r.failures, 0) + CASE WHEN
|
|
56
|
+
r.successes = coalesce(r.successes, 0) + CASE WHEN item.y >= 0.5 THEN 1 ELSE 0 END,
|
|
57
|
+
r.failures = coalesce(r.failures, 0) + CASE WHEN item.y < 0.5 THEN 1 ELSE 0 END
|
|
69
58
|
|
|
70
|
-
RETURN
|
|
59
|
+
RETURN item.memoryId AS id,
|
|
60
|
+
r.a AS a,
|
|
61
|
+
r.b AS b,
|
|
62
|
+
r.strength AS strength,
|
|
63
|
+
r.evidence AS evidence,
|
|
64
|
+
toString(r.updatedAt) AS updatedAt;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Parameters:
|
|
2
|
+
// - $ids: Array of memory ids
|
|
3
|
+
WITH [id IN coalesce($ids, []) WHERE id IS NOT NULL AND id <> ""] AS ids
|
|
4
|
+
UNWIND range(0, size(ids) - 1) AS idx
|
|
5
|
+
WITH idx, ids[idx] AS id
|
|
6
|
+
MATCH (m:Memory {id: id})
|
|
7
|
+
OPTIONAL MATCH (m)-[:APPLIES_IN]->(e:EnvironmentFingerprint)
|
|
8
|
+
WITH idx, m, collect(e {
|
|
9
|
+
.hash,
|
|
10
|
+
.os,
|
|
11
|
+
.distro,
|
|
12
|
+
.ci,
|
|
13
|
+
.container,
|
|
14
|
+
.filesystem,
|
|
15
|
+
.workspaceMount,
|
|
16
|
+
.nodeVersion,
|
|
17
|
+
.packageManager,
|
|
18
|
+
.pmVersion
|
|
19
|
+
}) AS envs
|
|
20
|
+
WITH collect({
|
|
21
|
+
idx: idx,
|
|
22
|
+
memory: m {
|
|
23
|
+
.id,
|
|
24
|
+
.kind,
|
|
25
|
+
.polarity,
|
|
26
|
+
.title,
|
|
27
|
+
.content,
|
|
28
|
+
.tags,
|
|
29
|
+
.confidence,
|
|
30
|
+
.utility,
|
|
31
|
+
.triage,
|
|
32
|
+
.antiPattern,
|
|
33
|
+
.createdAt,
|
|
34
|
+
.updatedAt,
|
|
35
|
+
env: envs[0]
|
|
36
|
+
}
|
|
37
|
+
}) AS rows
|
|
38
|
+
UNWIND rows AS row
|
|
39
|
+
WITH row
|
|
40
|
+
ORDER BY row.idx
|
|
41
|
+
RETURN collect(row.memory) AS memories;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Parameters:
|
|
2
|
+
// - $agentId: Agent id for RECALLS edges
|
|
3
|
+
// - $memoryIds: Array of memory ids
|
|
4
|
+
// - $includeNodes: Boolean to include node payloads
|
|
5
|
+
// - $includeRelatedTo: Boolean to include RELATED_TO edges
|
|
6
|
+
WITH
|
|
7
|
+
coalesce($agentId, "") AS agentId,
|
|
8
|
+
[id IN coalesce($memoryIds, []) WHERE id IS NOT NULL AND id <> ""] AS ids,
|
|
9
|
+
coalesce($includeNodes, true) AS includeNodes,
|
|
10
|
+
coalesce($includeRelatedTo, false) AS includeRelatedTo
|
|
11
|
+
|
|
12
|
+
CALL (ids, includeNodes) {
|
|
13
|
+
WITH ids, includeNodes
|
|
14
|
+
MATCH (m:Memory)
|
|
15
|
+
WHERE includeNodes = true AND m.id IN ids
|
|
16
|
+
OPTIONAL MATCH (m)-[:APPLIES_IN]->(e:EnvironmentFingerprint)
|
|
17
|
+
WITH m, collect(e {
|
|
18
|
+
.hash,
|
|
19
|
+
.os,
|
|
20
|
+
.distro,
|
|
21
|
+
.ci,
|
|
22
|
+
.container,
|
|
23
|
+
.filesystem,
|
|
24
|
+
.workspaceMount,
|
|
25
|
+
.nodeVersion,
|
|
26
|
+
.packageManager,
|
|
27
|
+
.pmVersion
|
|
28
|
+
}) AS envs
|
|
29
|
+
RETURN collect(m {
|
|
30
|
+
.id,
|
|
31
|
+
.kind,
|
|
32
|
+
.polarity,
|
|
33
|
+
.title,
|
|
34
|
+
.content,
|
|
35
|
+
.tags,
|
|
36
|
+
.confidence,
|
|
37
|
+
.utility,
|
|
38
|
+
.triage,
|
|
39
|
+
.antiPattern,
|
|
40
|
+
.createdAt,
|
|
41
|
+
.updatedAt,
|
|
42
|
+
env: envs[0]
|
|
43
|
+
}) AS nodes
|
|
44
|
+
|
|
45
|
+
UNION
|
|
46
|
+
|
|
47
|
+
WITH ids, includeNodes
|
|
48
|
+
WHERE includeNodes = false
|
|
49
|
+
RETURN [] AS nodes
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
CALL (ids, agentId) {
|
|
53
|
+
WITH ids, agentId
|
|
54
|
+
WHERE agentId IS NOT NULL AND agentId <> ""
|
|
55
|
+
MATCH (a:Agent {id: agentId})-[r:RECALLS]->(m:Memory)
|
|
56
|
+
WHERE m.id IN ids
|
|
57
|
+
RETURN collect({
|
|
58
|
+
source: a.id,
|
|
59
|
+
target: m.id,
|
|
60
|
+
kind: "recalls",
|
|
61
|
+
strength: r.strength,
|
|
62
|
+
evidence: r.evidence,
|
|
63
|
+
updatedAt: toString(r.updatedAt)
|
|
64
|
+
}) AS recallEdges
|
|
65
|
+
|
|
66
|
+
UNION
|
|
67
|
+
|
|
68
|
+
WITH ids, agentId
|
|
69
|
+
WHERE agentId IS NULL OR agentId = ""
|
|
70
|
+
RETURN [] AS recallEdges
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
CALL (ids) {
|
|
74
|
+
WITH ids
|
|
75
|
+
MATCH (m1:Memory)-[c:CO_USED_WITH]->(m2:Memory)
|
|
76
|
+
WHERE m1.id IN ids AND m2.id IN ids
|
|
77
|
+
RETURN collect({
|
|
78
|
+
source: m1.id,
|
|
79
|
+
target: m2.id,
|
|
80
|
+
kind: "co_used_with",
|
|
81
|
+
strength: c.strength,
|
|
82
|
+
evidence: c.evidence,
|
|
83
|
+
updatedAt: toString(c.updatedAt)
|
|
84
|
+
}) AS coUsedEdges
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
CALL (ids, includeRelatedTo) {
|
|
88
|
+
WITH ids, includeRelatedTo
|
|
89
|
+
WHERE includeRelatedTo = true
|
|
90
|
+
MATCH (m1:Memory)-[r:RELATED_TO]->(m2:Memory)
|
|
91
|
+
WHERE m1.id IN ids AND m2.id IN ids
|
|
92
|
+
RETURN collect({
|
|
93
|
+
source: m1.id,
|
|
94
|
+
target: m2.id,
|
|
95
|
+
kind: "related_to",
|
|
96
|
+
strength: r.weight,
|
|
97
|
+
evidence: 0.0,
|
|
98
|
+
updatedAt: toString(r.updatedAt)
|
|
99
|
+
}) AS relatedEdges
|
|
100
|
+
|
|
101
|
+
UNION
|
|
102
|
+
|
|
103
|
+
WITH ids, includeRelatedTo
|
|
104
|
+
WHERE includeRelatedTo = false
|
|
105
|
+
RETURN [] AS relatedEdges
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
RETURN nodes, recallEdges + coUsedEdges + relatedEdges AS edges;
|
package/dist/cypher/index.ts
CHANGED
|
@@ -21,4 +21,8 @@ export const cypher = {
|
|
|
21
21
|
listMemories: loadCypher("list_memories.cypher"),
|
|
22
22
|
relateConcepts: loadCypher("relate_concepts.cypher"),
|
|
23
23
|
autoRelateByTags: loadCypher("auto_relate_memory_by_tags.cypher"),
|
|
24
|
+
getMemoriesById: loadCypher("get_memories_by_id.cypher"),
|
|
25
|
+
getMemoryGraph: loadCypher("get_memory_graph.cypher"),
|
|
26
|
+
fallbackRetrieveMemories: loadCypher("fallback_retrieve_memories.cypher"),
|
|
27
|
+
listMemoryEdges: loadCypher("list_memory_edges.cypher"),
|
|
24
28
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Parameters:
|
|
2
|
+
// - $limit: Max edges to return
|
|
3
|
+
// - $minStrength: Minimum strength threshold
|
|
4
|
+
WITH
|
|
5
|
+
coalesce($limit, 200) AS limit,
|
|
6
|
+
coalesce($minStrength, 0.0) AS minStrength
|
|
7
|
+
|
|
8
|
+
CALL {
|
|
9
|
+
WITH minStrength
|
|
10
|
+
MATCH (m1:Memory)-[c:CO_USED_WITH]->(m2:Memory)
|
|
11
|
+
WHERE coalesce(c.strength, 0.0) >= minStrength
|
|
12
|
+
RETURN {
|
|
13
|
+
source: m1.id,
|
|
14
|
+
target: m2.id,
|
|
15
|
+
kind: "co_used_with",
|
|
16
|
+
strength: c.strength,
|
|
17
|
+
evidence: c.evidence,
|
|
18
|
+
updatedAt: toString(c.updatedAt)
|
|
19
|
+
} AS edge
|
|
20
|
+
|
|
21
|
+
UNION
|
|
22
|
+
|
|
23
|
+
WITH minStrength
|
|
24
|
+
MATCH (m1:Memory)-[r:RELATED_TO]->(m2:Memory)
|
|
25
|
+
WHERE coalesce(r.weight, 0.0) >= minStrength
|
|
26
|
+
RETURN {
|
|
27
|
+
source: m1.id,
|
|
28
|
+
target: m2.id,
|
|
29
|
+
kind: "related_to",
|
|
30
|
+
strength: r.weight,
|
|
31
|
+
evidence: 0.0,
|
|
32
|
+
updatedAt: toString(r.updatedAt)
|
|
33
|
+
} AS edge
|
|
34
|
+
}
|
|
35
|
+
WITH edge, limit
|
|
36
|
+
ORDER BY edge.strength DESC
|
|
37
|
+
RETURN collect(edge)[0..limit] AS edges;
|