Skip to content

Feat: neo4j community #121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 194 additions & 8 deletions examples/basic_modules/neo4j_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Multi-UAV Long-Term Coverage",
value="Research topic on distributed multi-agent UAV navigation and coverage",
hierarchy_level="topic",
type="fact",
memory_time="2024-01-01",
Expand Down Expand Up @@ -74,7 +73,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Reward Function Design",
value="Combines coverage, energy efficiency, and overlap penalty",
hierarchy_level="concept",
type="fact",
memory_time="2024-01-01",
Expand All @@ -99,7 +97,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Energy Model",
value="Includes communication and motion energy consumption",
hierarchy_level="concept",
type="fact",
memory_time="2024-01-01",
Expand All @@ -122,7 +119,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Coverage Metrics",
value="CT and FT used for long-term area and fairness evaluation",
hierarchy_level="concept",
type="fact",
memory_time="2024-01-01",
Expand Down Expand Up @@ -161,7 +157,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="WorkingMemory",
key="Reward Components",
value="Coverage gain, energy usage penalty, overlap penalty",
hierarchy_level="fact",
type="fact",
memory_time="2024-01-01",
Expand All @@ -186,7 +181,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Energy Cost Components",
value="Includes movement and communication energy",
hierarchy_level="fact",
type="fact",
memory_time="2024-01-01",
Expand All @@ -211,7 +205,6 @@ def example_multi_db(db_name: str = "paper"):
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="CT and FT Definition",
value="CT: total coverage duration; FT: fairness index",
hierarchy_level="fact",
type="fact",
memory_time="2024-01-01",
Expand Down Expand Up @@ -347,9 +340,202 @@ def example_shared_db(db_name: str = "shared-traval-group"):
print(graph_alice.get_node(node["id"]))


def run_user_session(
user_name: str,
db_name: str,
topic_text: str,
concept_texts: list[str],
fact_texts: list[str],
community: bool = False,
):
print(f"\n=== {user_name} starts building their memory graph ===")

# Manually initialize correct GraphDB class
if community:
config = GraphDBConfigFactory(
backend="neo4j-community",
config={
"uri": "bolt://localhost:7687",
"user": "neo4j",
"password": "12345678",
"db_name": db_name,
"user_name": user_name,
"use_multi_db": False,
"auto_create": False, # Neo4j Community does not allow auto DB creation
"embedding_dimension": 768,
"vec_config": {
# Pass nested config to initialize external vector DB
# If you use qdrant, please use Server instead of local mode.
"backend": "qdrant",
"config": {
"collection_name": "neo4j_vec_db",
"vector_dimension": 768,
"distance_metric": "cosine",
"host": "localhost",
"port": 6333,
},
},
},
)
else:
config = GraphDBConfigFactory(
backend="neo4j",
config={
"uri": "bolt://localhost:7687",
"user": "neo4j",
"password": "12345678",
"db_name": db_name,
"user_name": user_name,
"use_multi_db": False,
"auto_create": True,
"embedding_dimension": 768,
},
)
graph = GraphStoreFactory.from_config(config)

# Start with a clean slate for this user
graph.clear()

now = datetime.utcnow().isoformat()

# === Step 1: Create a root topic node (e.g., user's research focus) ===
topic = TextualMemoryItem(
memory=topic_text,
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key="Research Topic",
hierarchy_level="topic",
type="fact",
memory_time="2024-01-01",
status="activated",
visibility="public",
updated_at=now,
embedding=embed_memory_item(topic_text),
),
)
graph.add_node(topic.id, topic.memory, topic.metadata.model_dump(exclude_none=True))

# === Step 2: Create two concept nodes linked to the topic ===
concept_items = []
for i, text in enumerate(concept_texts):
concept = TextualMemoryItem(
memory=text,
metadata=TreeNodeTextualMemoryMetadata(
memory_type="LongTermMemory",
key=f"Concept {i + 1}",
hierarchy_level="concept",
type="fact",
memory_time="2024-01-01",
status="activated",
visibility="public",
updated_at=now,
embedding=embed_memory_item(text),
tags=["concept"],
confidence=90 + i,
),
)
graph.add_node(concept.id, concept.memory, concept.metadata.model_dump(exclude_none=True))
graph.add_edge(topic.id, concept.id, type="PARENT")
concept_items.append(concept)

# === Step 3: Create supporting facts under each concept ===
for i, text in enumerate(fact_texts):
fact = TextualMemoryItem(
memory=text,
metadata=TreeNodeTextualMemoryMetadata(
memory_type="WorkingMemory",
key=f"Fact {i + 1}",
hierarchy_level="fact",
type="fact",
memory_time="2024-01-01",
status="activated",
visibility="public",
updated_at=now,
embedding=embed_memory_item(text),
confidence=85.0,
tags=["fact"],
),
)
graph.add_node(fact.id, fact.memory, fact.metadata.model_dump(exclude_none=True))
graph.add_edge(concept_items[i % len(concept_items)].id, fact.id, type="PARENT")

# === Step 4: Retrieve memory using semantic search ===
vector = embed_memory_item("How is memory retrieved?")
search_result = graph.search_by_embedding(vector, top_k=2)
for r in search_result:
node = graph.get_node(r["id"])
print("🔍 Search result:", node["memory"])

# === Step 5: Tag-based neighborhood discovery ===
neighbors = graph.get_neighbors_by_tag(["concept"], exclude_ids=[], top_k=2)
print("📎 Tag-related nodes:", [neighbor["memory"] for neighbor in neighbors])

# === Step 6: Retrieve children (facts) of first concept ===
children = graph.get_children_with_embeddings(concept_items[0].id)
print("📍 Children of concept:", [child["memory"] for child in children])

# === Step 7: Export a local subgraph and grouped statistics ===
subgraph = graph.get_subgraph(topic.id, depth=2)
print("📌 Subgraph node count:", len(subgraph["neighbors"]))

stats = graph.get_grouped_counts(["memory_type", "status"])
print("📊 Grouped counts:", stats)

# === Step 8: Demonstrate updates and cleanup ===
graph.update_node(concept_items[0].id, {"confidence": 99.0})
graph.remove_oldest_memory("WorkingMemory", keep_latest=1)
graph.delete_edge(topic.id, concept_items[0].id, type="PARENT")
graph.delete_node(concept_items[1].id)

# === Step 9: Export and re-import the entire graph structure ===
exported = graph.export_graph()
graph.import_graph(exported)
print("📦 Graph exported and re-imported, total nodes:", len(exported["nodes"]))


def example_complex_shared_db(db_name: str = "shared-traval-group-complex", community=False):
# User 1: Alice explores structured memory for LLMs
run_user_session(
user_name="alice",
db_name=db_name,
topic_text="Alice studies structured memory and long-term memory optimization in LLMs.",
concept_texts=[
"Short-term memory can be simulated using WorkingMemory blocks.",
"A structured memory graph improves retrieval precision for agents.",
],
fact_texts=[
"Embedding search is used to find semantically similar memory items.",
"User memories are stored as node-edge structures that support hierarchical reasoning.",
],
community=community,
)

# User 2: Bob focuses on GNN-based reasoning
run_user_session(
user_name="bob",
db_name=db_name,
topic_text="Bob investigates how graph neural networks can support knowledge reasoning.",
concept_texts=[
"GNNs can learn high-order relations among entities.",
"Attention mechanisms in graphs improve inference precision.",
],
fact_texts=[
"GAT outperforms GCN in graph classification tasks.",
"Multi-hop reasoning helps answer complex queries.",
],
community=community,
)


if __name__ == "__main__":
print("\n=== Example: Multi-DB ===")
example_multi_db(db_name="paper")

print("\n=== Example: Single-DB ===")
example_shared_db(db_name="shared-traval-group11")
example_shared_db(db_name="shared-traval-group")

print("\n=== Example: Single-DB-Complex ===")
example_complex_shared_db(db_name="shared-traval-group-complex-new")

print("\n=== Example: Single-Community-DB-Complex ===")
example_complex_shared_db(db_name="paper", community=True)
50 changes: 50 additions & 0 deletions examples/data/config/tree_config_community.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"extractor_llm": {
"backend": "ollama",
"config": {
"model_name_or_path": "qwen3:0.6b",
"temperature": 0.0,
"remove_think_prefix": true,
"max_tokens": 8192
}
},
"dispatcher_llm": {
"backend": "ollama",
"config": {
"model_name_or_path": "qwen3:0.6b",
"temperature": 0.0,
"remove_think_prefix": true,
"max_tokens": 8192
}
},
"embedder": {
"backend": "ollama",
"config": {
"model_name_or_path": "nomic-embed-text:latest"
}
},
"graph_db": {
"backend": "neo4j-community",
"config": {
"uri": "bolt://localhost:7687",
"user": "neo4j",
"password": "12345678",
"db_name": "neo4j",
"user_name": "alice",
"use_multi_db": false,
"auto_create": false,
"embedding_dimension": 768,
"vec_config": {
"backend": "qdrant",
"config": {
"collection_name": "neo4j_vec_db",
"vector_dimension": 768,
"distance_metric": "cosine",
"host": "localhost",
"port": 6333
}
}
}
},
"reorganize": true
}
Loading