Skip to content

Commit 956a9d7

Browse files
authored
Feat: neo4j community (#121)
* feat: init neo4j-community * feat: Inherit existing class for Community Edition database support * feat: add outer vector db * feat: finish neo4j community * feat: add tree community config example * fix: ensure outer db for neo4j-community is server mode * feat: update tree-config-community * fix: bug for qdrant delete * fix: create index for outer_db * feat: add ensure payload index exists in qdrant * feat: create index for qdrant * feat: add simple_openapi_memos_neo4j_community
1 parent 56d6fd5 commit 956a9d7

File tree

10 files changed

+938
-29
lines changed

10 files changed

+938
-29
lines changed

examples/basic_modules/neo4j_example.py

Lines changed: 194 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ def example_multi_db(db_name: str = "paper"):
4242
metadata=TreeNodeTextualMemoryMetadata(
4343
memory_type="LongTermMemory",
4444
key="Multi-UAV Long-Term Coverage",
45-
value="Research topic on distributed multi-agent UAV navigation and coverage",
4645
hierarchy_level="topic",
4746
type="fact",
4847
memory_time="2024-01-01",
@@ -74,7 +73,6 @@ def example_multi_db(db_name: str = "paper"):
7473
metadata=TreeNodeTextualMemoryMetadata(
7574
memory_type="LongTermMemory",
7675
key="Reward Function Design",
77-
value="Combines coverage, energy efficiency, and overlap penalty",
7876
hierarchy_level="concept",
7977
type="fact",
8078
memory_time="2024-01-01",
@@ -99,7 +97,6 @@ def example_multi_db(db_name: str = "paper"):
9997
metadata=TreeNodeTextualMemoryMetadata(
10098
memory_type="LongTermMemory",
10199
key="Energy Model",
102-
value="Includes communication and motion energy consumption",
103100
hierarchy_level="concept",
104101
type="fact",
105102
memory_time="2024-01-01",
@@ -122,7 +119,6 @@ def example_multi_db(db_name: str = "paper"):
122119
metadata=TreeNodeTextualMemoryMetadata(
123120
memory_type="LongTermMemory",
124121
key="Coverage Metrics",
125-
value="CT and FT used for long-term area and fairness evaluation",
126122
hierarchy_level="concept",
127123
type="fact",
128124
memory_time="2024-01-01",
@@ -161,7 +157,6 @@ def example_multi_db(db_name: str = "paper"):
161157
metadata=TreeNodeTextualMemoryMetadata(
162158
memory_type="WorkingMemory",
163159
key="Reward Components",
164-
value="Coverage gain, energy usage penalty, overlap penalty",
165160
hierarchy_level="fact",
166161
type="fact",
167162
memory_time="2024-01-01",
@@ -186,7 +181,6 @@ def example_multi_db(db_name: str = "paper"):
186181
metadata=TreeNodeTextualMemoryMetadata(
187182
memory_type="LongTermMemory",
188183
key="Energy Cost Components",
189-
value="Includes movement and communication energy",
190184
hierarchy_level="fact",
191185
type="fact",
192186
memory_time="2024-01-01",
@@ -211,7 +205,6 @@ def example_multi_db(db_name: str = "paper"):
211205
metadata=TreeNodeTextualMemoryMetadata(
212206
memory_type="LongTermMemory",
213207
key="CT and FT Definition",
214-
value="CT: total coverage duration; FT: fairness index",
215208
hierarchy_level="fact",
216209
type="fact",
217210
memory_time="2024-01-01",
@@ -347,9 +340,202 @@ def example_shared_db(db_name: str = "shared-traval-group"):
347340
print(graph_alice.get_node(node["id"]))
348341

349342

343+
def run_user_session(
344+
user_name: str,
345+
db_name: str,
346+
topic_text: str,
347+
concept_texts: list[str],
348+
fact_texts: list[str],
349+
community: bool = False,
350+
):
351+
print(f"\n=== {user_name} starts building their memory graph ===")
352+
353+
# Manually initialize correct GraphDB class
354+
if community:
355+
config = GraphDBConfigFactory(
356+
backend="neo4j-community",
357+
config={
358+
"uri": "bolt://localhost:7687",
359+
"user": "neo4j",
360+
"password": "12345678",
361+
"db_name": db_name,
362+
"user_name": user_name,
363+
"use_multi_db": False,
364+
"auto_create": False, # Neo4j Community does not allow auto DB creation
365+
"embedding_dimension": 768,
366+
"vec_config": {
367+
# Pass nested config to initialize external vector DB
368+
# If you use qdrant, please use Server instead of local mode.
369+
"backend": "qdrant",
370+
"config": {
371+
"collection_name": "neo4j_vec_db",
372+
"vector_dimension": 768,
373+
"distance_metric": "cosine",
374+
"host": "localhost",
375+
"port": 6333,
376+
},
377+
},
378+
},
379+
)
380+
else:
381+
config = GraphDBConfigFactory(
382+
backend="neo4j",
383+
config={
384+
"uri": "bolt://localhost:7687",
385+
"user": "neo4j",
386+
"password": "12345678",
387+
"db_name": db_name,
388+
"user_name": user_name,
389+
"use_multi_db": False,
390+
"auto_create": True,
391+
"embedding_dimension": 768,
392+
},
393+
)
394+
graph = GraphStoreFactory.from_config(config)
395+
396+
# Start with a clean slate for this user
397+
graph.clear()
398+
399+
now = datetime.utcnow().isoformat()
400+
401+
# === Step 1: Create a root topic node (e.g., user's research focus) ===
402+
topic = TextualMemoryItem(
403+
memory=topic_text,
404+
metadata=TreeNodeTextualMemoryMetadata(
405+
memory_type="LongTermMemory",
406+
key="Research Topic",
407+
hierarchy_level="topic",
408+
type="fact",
409+
memory_time="2024-01-01",
410+
status="activated",
411+
visibility="public",
412+
updated_at=now,
413+
embedding=embed_memory_item(topic_text),
414+
),
415+
)
416+
graph.add_node(topic.id, topic.memory, topic.metadata.model_dump(exclude_none=True))
417+
418+
# === Step 2: Create two concept nodes linked to the topic ===
419+
concept_items = []
420+
for i, text in enumerate(concept_texts):
421+
concept = TextualMemoryItem(
422+
memory=text,
423+
metadata=TreeNodeTextualMemoryMetadata(
424+
memory_type="LongTermMemory",
425+
key=f"Concept {i + 1}",
426+
hierarchy_level="concept",
427+
type="fact",
428+
memory_time="2024-01-01",
429+
status="activated",
430+
visibility="public",
431+
updated_at=now,
432+
embedding=embed_memory_item(text),
433+
tags=["concept"],
434+
confidence=90 + i,
435+
),
436+
)
437+
graph.add_node(concept.id, concept.memory, concept.metadata.model_dump(exclude_none=True))
438+
graph.add_edge(topic.id, concept.id, type="PARENT")
439+
concept_items.append(concept)
440+
441+
# === Step 3: Create supporting facts under each concept ===
442+
for i, text in enumerate(fact_texts):
443+
fact = TextualMemoryItem(
444+
memory=text,
445+
metadata=TreeNodeTextualMemoryMetadata(
446+
memory_type="WorkingMemory",
447+
key=f"Fact {i + 1}",
448+
hierarchy_level="fact",
449+
type="fact",
450+
memory_time="2024-01-01",
451+
status="activated",
452+
visibility="public",
453+
updated_at=now,
454+
embedding=embed_memory_item(text),
455+
confidence=85.0,
456+
tags=["fact"],
457+
),
458+
)
459+
graph.add_node(fact.id, fact.memory, fact.metadata.model_dump(exclude_none=True))
460+
graph.add_edge(concept_items[i % len(concept_items)].id, fact.id, type="PARENT")
461+
462+
# === Step 4: Retrieve memory using semantic search ===
463+
vector = embed_memory_item("How is memory retrieved?")
464+
search_result = graph.search_by_embedding(vector, top_k=2)
465+
for r in search_result:
466+
node = graph.get_node(r["id"])
467+
print("🔍 Search result:", node["memory"])
468+
469+
# === Step 5: Tag-based neighborhood discovery ===
470+
neighbors = graph.get_neighbors_by_tag(["concept"], exclude_ids=[], top_k=2)
471+
print("📎 Tag-related nodes:", [neighbor["memory"] for neighbor in neighbors])
472+
473+
# === Step 6: Retrieve children (facts) of first concept ===
474+
children = graph.get_children_with_embeddings(concept_items[0].id)
475+
print("📍 Children of concept:", [child["memory"] for child in children])
476+
477+
# === Step 7: Export a local subgraph and grouped statistics ===
478+
subgraph = graph.get_subgraph(topic.id, depth=2)
479+
print("📌 Subgraph node count:", len(subgraph["neighbors"]))
480+
481+
stats = graph.get_grouped_counts(["memory_type", "status"])
482+
print("📊 Grouped counts:", stats)
483+
484+
# === Step 8: Demonstrate updates and cleanup ===
485+
graph.update_node(concept_items[0].id, {"confidence": 99.0})
486+
graph.remove_oldest_memory("WorkingMemory", keep_latest=1)
487+
graph.delete_edge(topic.id, concept_items[0].id, type="PARENT")
488+
graph.delete_node(concept_items[1].id)
489+
490+
# === Step 9: Export and re-import the entire graph structure ===
491+
exported = graph.export_graph()
492+
graph.import_graph(exported)
493+
print("📦 Graph exported and re-imported, total nodes:", len(exported["nodes"]))
494+
495+
496+
def example_complex_shared_db(db_name: str = "shared-traval-group-complex", community=False):
497+
# User 1: Alice explores structured memory for LLMs
498+
run_user_session(
499+
user_name="alice",
500+
db_name=db_name,
501+
topic_text="Alice studies structured memory and long-term memory optimization in LLMs.",
502+
concept_texts=[
503+
"Short-term memory can be simulated using WorkingMemory blocks.",
504+
"A structured memory graph improves retrieval precision for agents.",
505+
],
506+
fact_texts=[
507+
"Embedding search is used to find semantically similar memory items.",
508+
"User memories are stored as node-edge structures that support hierarchical reasoning.",
509+
],
510+
community=community,
511+
)
512+
513+
# User 2: Bob focuses on GNN-based reasoning
514+
run_user_session(
515+
user_name="bob",
516+
db_name=db_name,
517+
topic_text="Bob investigates how graph neural networks can support knowledge reasoning.",
518+
concept_texts=[
519+
"GNNs can learn high-order relations among entities.",
520+
"Attention mechanisms in graphs improve inference precision.",
521+
],
522+
fact_texts=[
523+
"GAT outperforms GCN in graph classification tasks.",
524+
"Multi-hop reasoning helps answer complex queries.",
525+
],
526+
community=community,
527+
)
528+
529+
350530
if __name__ == "__main__":
351531
print("\n=== Example: Multi-DB ===")
352532
example_multi_db(db_name="paper")
353533

354534
print("\n=== Example: Single-DB ===")
355-
example_shared_db(db_name="shared-traval-group11")
535+
example_shared_db(db_name="shared-traval-group")
536+
537+
print("\n=== Example: Single-DB-Complex ===")
538+
example_complex_shared_db(db_name="shared-traval-group-complex-new")
539+
540+
print("\n=== Example: Single-Community-DB-Complex ===")
541+
example_complex_shared_db(db_name="paper", community=True)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"extractor_llm": {
3+
"backend": "ollama",
4+
"config": {
5+
"model_name_or_path": "qwen3:0.6b",
6+
"temperature": 0.0,
7+
"remove_think_prefix": true,
8+
"max_tokens": 8192
9+
}
10+
},
11+
"dispatcher_llm": {
12+
"backend": "ollama",
13+
"config": {
14+
"model_name_or_path": "qwen3:0.6b",
15+
"temperature": 0.0,
16+
"remove_think_prefix": true,
17+
"max_tokens": 8192
18+
}
19+
},
20+
"embedder": {
21+
"backend": "ollama",
22+
"config": {
23+
"model_name_or_path": "nomic-embed-text:latest"
24+
}
25+
},
26+
"graph_db": {
27+
"backend": "neo4j-community",
28+
"config": {
29+
"uri": "bolt://localhost:7687",
30+
"user": "neo4j",
31+
"password": "12345678",
32+
"db_name": "neo4j",
33+
"user_name": "alice",
34+
"use_multi_db": false,
35+
"auto_create": false,
36+
"embedding_dimension": 768,
37+
"vec_config": {
38+
"backend": "qdrant",
39+
"config": {
40+
"collection_name": "neo4j_vec_db",
41+
"vector_dimension": 768,
42+
"distance_metric": "cosine",
43+
"host": "localhost",
44+
"port": 6333
45+
}
46+
}
47+
}
48+
},
49+
"reorganize": true
50+
}

0 commit comments

Comments
 (0)