Skip to content
Draft
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
15 changes: 15 additions & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ jobs:
NEO4J_AUTH: neo4j/testpass
NEO4J_PLUGINS: '["apoc"]'
options: --health-cmd "cypher-shell -u neo4j -p testpass 'RETURN 1'" --health-interval 10s --health-timeout 5s --health-retries 10
memgraph:
image: memgraph/memgraph:latest
ports:
- 7688:7687
options: --health-cmd "mg_client --host localhost --port 7687 --use-ssl=false --query 'RETURN 1;'" --health-interval 10s --health-timeout 5s --health-retries 10
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand Down Expand Up @@ -98,3 +103,13 @@ jobs:
tests/cross_encoder/test_bge_reranker_client_int.py \
tests/driver/test_falkordb_driver.py \
-m "not integration"
- name: Run Memgraph integration tests
env:
PYTHONPATH: ${{ github.workspace }}
MEMGRAPH_URI: bolt://localhost:7688
MEMGRAPH_USER:
MEMGRAPH_PASSWORD:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
uv run pytest tests/test_*_int.py -k "memgraph"

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ particularly suitable for applications requiring real-time interaction and preci
Requirements:

- Python 3.10 or higher
- Neo4j 5.26 / FalkorDB 1.1.2 / Kuzu 0.11.2 / Amazon Neptune Database Cluster or Neptune Analytics Graph + Amazon
- Neo4j 5.26 / FalkorDB 1.1.2 / Kuzu 0.11.2 / Memgraph 2.22+ / Amazon Neptune Database Cluster or Neptune Analytics Graph + Amazon
OpenSearch Serverless collection (serves as the full text search backend)
- OpenAI API key (Graphiti defaults to OpenAI for LLM inference and embedding)

Expand Down Expand Up @@ -564,7 +564,7 @@ When you initialize a Graphiti instance, we collect:
- **Graphiti version**: The version you're using
- **Configuration choices**:
- LLM provider type (OpenAI, Azure, Anthropic, etc.)
- Database backend (Neo4j, FalkorDB, Kuzu, Amazon Neptune Database or Neptune Analytics)
- Database backend (Neo4j, FalkorDB, Kuzu, Memgraph, Amazon Neptune Database or Neptune Analytics)
- Embedder provider (OpenAI, Azure, Voyage, etc.)

### What We Don't Collect
Expand Down
29 changes: 29 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,35 @@ services:
- PORT=8001
- db_backend=falkordb

memgraph:
image: memgraph/memgraph:latest
healthcheck:
test:
[
"CMD",
"mg_client",
"--host",
"localhost",
"--port",
"7687",
"--use-ssl=false",
"--query",
"RETURN 1;"
]
interval: 5s
timeout: 10s
retries: 10
start_period: 3s
ports:
- "7688:7687" # Bolt (using different port to avoid conflict)
volumes:
- memgraph_data:/var/lib/memgraph
environment:
- MEMGRAPH_USER=${MEMGRAPH_USER:-}
- MEMGRAPH_PASSWORD=${MEMGRAPH_PASSWORD:-}
command: ["--log-level=TRACE", "--also-log-to-stderr", "--bolt-port=7687"]

volumes:
neo4j_data:
memgraph_data:
falkordb_data:
15 changes: 13 additions & 2 deletions examples/quickstart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This example demonstrates the basic functionality of Graphiti, including:

1. Connecting to a Neo4j or FalkorDB database
1. Connecting to a Neo4j, FalkorDB, or Memgraph database
2. Initializing Graphiti indices and constraints
3. Adding episodes to the graph
4. Searching the graph with semantic and keyword matching
Expand All @@ -17,6 +17,9 @@ This example demonstrates the basic functionality of Graphiti, including:
- Neo4j Desktop installed and running
- A local DBMS created and started in Neo4j Desktop
- **For FalkorDB**:
- FalkorDB server running (see [FalkorDB documentation](https://falkordb.com/docs/) for setup)
- **For Memgraph**:
- Memgraph server running (see [Memgraph documentation](https://memgraph.com/docs/) for setup)
- FalkorDB server running (see [FalkorDB documentation](https://docs.falkordb.com) for setup)
- **For Amazon Neptune**:
- Amazon server running (see [Amazon Neptune documentation](https://aws.amazon.com/neptune/developer-resources/) for setup)
Expand Down Expand Up @@ -44,6 +47,11 @@ export NEO4J_PASSWORD=password
# Optional FalkorDB connection parameters (defaults shown)
export FALKORDB_URI=falkor://localhost:6379

# Optional Memgraph connection parameters (defaults shown)
export MEMGRAPH_URI=bolt://localhost:7687
export MEMGRAPH_USER= # Optional - Memgraph doesn't require auth by default
export MEMGRAPH_PASSWORD= # Optional - Memgraph doesn't require auth by default

# Optional Amazon Neptune connection parameters
NEPTUNE_HOST=your_neptune_host
NEPTUNE_PORT=your_port_or_8182
Expand All @@ -65,13 +73,16 @@ python quickstart_neo4j.py
# For FalkorDB
python quickstart_falkordb.py

# For Memgraph
python quickstart_memgraph.py

# For Amazon Neptune
python quickstart_neptune.py
```

## What This Example Demonstrates

- **Graph Initialization**: Setting up the Graphiti indices and constraints in Neo4j, Amazon Neptune, or FalkorDB
- **Graph Initialization**: Setting up the Graphiti indices and constraints in Neo4j, FalkorDB, Memgraph, or Amazon Neptune
- **Adding Episodes**: Adding text content that will be analyzed and converted into knowledge graph nodes and edges
- **Edge Search Functionality**: Performing hybrid searches that combine semantic similarity and BM25 retrieval to find relationships (edges)
- **Graph-Aware Search**: Using the source node UUID from the top search result to rerank additional search results based on graph distance
Expand Down
254 changes: 254 additions & 0 deletions examples/quickstart/quickstart_memgraph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
"""
Copyright 2025, Zep Software, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import asyncio
import json
import logging
import os
from datetime import datetime, timezone
from logging import INFO

from dotenv import load_dotenv

from graphiti_core import Graphiti
from graphiti_core.driver.memgraph_driver import MemgraphDriver
from graphiti_core.nodes import EpisodeType
from graphiti_core.search.search_config_recipes import NODE_HYBRID_SEARCH_RRF

#################################################
# CONFIGURATION
#################################################
# Set up logging and environment variables for
# connecting to Memgraph database
#################################################

# Configure logging
logging.basicConfig(
level=INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
logger = logging.getLogger(__name__)

load_dotenv()

# Memgraph connection parameters
# Make sure Memgraph is running (default port 7687, same as Neo4j)
memgraph_uri = os.environ.get('MEMGRAPH_URI', 'bolt://localhost:7687')
memgraph_user = os.environ.get(
'MEMGRAPH_USER', ''
) # Memgraph often doesn't require auth by default
memgraph_password = os.environ.get('MEMGRAPH_PASSWORD', '')

if not memgraph_uri:
raise ValueError('MEMGRAPH_URI must be set')


async def main():
#################################################
# INITIALIZATION
#################################################
# Connect to Memgraph and set up Graphiti indices
# This is required before using other Graphiti
# functionality
#################################################

# Initialize Memgraph driver
memgraph_driver = MemgraphDriver(memgraph_uri, memgraph_user, memgraph_password)

# Initialize Graphiti with Memgraph connection
graphiti = Graphiti(graph_driver=memgraph_driver)

try:
# Initialize the graph database with graphiti's indices. This only needs to be done once.
await graphiti.build_indices_and_constraints()

#################################################
# ADDING EPISODES
#################################################
# Episodes are the primary units of information
# in Graphiti. They can be text or structured JSON
# and are automatically processed to extract entities
# and relationships.
#################################################

# Example: Add Episodes
# Episodes list containing both text and JSON episodes
episodes = [
{
'content': 'Kamala Harris is the Attorney General of California. She was previously '
'the district attorney for San Francisco.',
'type': EpisodeType.text,
'description': 'podcast transcript',
},
{
'content': 'As AG, Harris was in office from January 3, 2011 – January 3, 2017',
'type': EpisodeType.text,
'description': 'podcast transcript',
},
{
'content': {
'name': 'Gavin Newsom',
'position': 'Governor',
'state': 'California',
'predecessor': 'Jerry Brown',
},
'type': EpisodeType.json,
'description': 'politician info',
},
{
'content': 'Jerry Brown was the predecessor of Gavin Newsom as Governor of California',
'type': EpisodeType.text,
'description': 'podcast transcript',
},
]

# Add episodes to the graph
for i, episode in enumerate(episodes):
await graphiti.add_episode(
name=f'Memgraph Demo {i}',
episode_body=episode['content']
if isinstance(episode['content'], str)
else json.dumps(episode['content']),
source=episode['type'],
source_description=episode['description'],
reference_time=datetime.now(timezone.utc),
)
logger.info(f'Added episode: Memgraph Demo {i} ({episode["type"].value})')

logger.info('Episodes added successfully!')

#################################################
# BASIC SEARCH
#################################################
# The simplest way to retrieve relationships (edges)
# from Graphiti is using the search method, which
# performs a hybrid search combining semantic
# similarity and BM25 text retrieval.
#################################################

# Perform a hybrid search combining semantic similarity and BM25 retrieval
logger.info('\n=== BASIC SEARCH ===')
search_query = 'Who was the California Attorney General?'
logger.info(f'Search query: {search_query}')

# Perform semantic search across edges (relationships)
results = await graphiti.search(search_query)

logger.info('Search results:')
for result in results[:3]: # Show top 3 results
logger.info(f'UUID: {result.uuid}')
logger.info(f'Fact: {result.fact}')
if hasattr(result, 'valid_at') and result.valid_at:
logger.info(f'Valid from: {result.valid_at}')
if hasattr(result, 'invalid_at') and result.invalid_at:
logger.info(f'Valid until: {result.invalid_at}')
logger.info('---')

#################################################
# CENTER NODE SEARCH
#################################################
# For more contextually relevant results, you can
# use a center node to rerank search results based
# on their graph distance to a specific node
#################################################

if results and len(results) > 0:
logger.info('\n=== CENTER NODE SEARCH ===')
# Get the source node UUID from the top result
center_node_uuid = results[0].source_node_uuid
logger.info(f'Using center node UUID: {center_node_uuid}')

# Perform graph-based search using the source node
reranked_results = await graphiti.search(
search_query, center_node_uuid=center_node_uuid
)

logger.info('Reranked search results:')
for result in reranked_results[:3]:
logger.info(f'UUID: {result.uuid}')
logger.info(f'Fact: {result.fact}')
if hasattr(result, 'valid_at') and result.valid_at:
logger.info(f'Valid from: {result.valid_at}')
if hasattr(result, 'invalid_at') and result.invalid_at:
logger.info(f'Valid until: {result.invalid_at}')
logger.info('---')
else:
logger.info('No results found in the initial search to use as center node.')

#################################################
# NODE SEARCH WITH RECIPES
#################################################
# Graphiti provides predefined search configurations
# (recipes) that optimize search for specific patterns
# and use cases.
#################################################

logger.info('\n=== NODE SEARCH WITH RECIPES ===')
recipe_query = 'California Governor'
logger.info(f'Recipe search query: {recipe_query}')

# Use hybrid search recipe for balanced semantic and keyword matching
node_search_config = NODE_HYBRID_SEARCH_RRF.model_copy(deep=True)
node_search_config.limit = 5 # Limit to 5 results

# Execute the node search
node_search_results = await graphiti._search(
query=recipe_query,
config=node_search_config,
)

logger.info('Node search results:')
for node in node_search_results.nodes:
logger.info(f'Node UUID: {node.uuid}')
logger.info(f'Node Name: {node.name}')
node_summary = node.summary[:100] + '...' if len(node.summary) > 100 else node.summary
logger.info(f'Content Summary: {node_summary}')
logger.info(f'Node Labels: {", ".join(node.labels)}')
logger.info(f'Created At: {node.created_at}')
if hasattr(node, 'attributes') and node.attributes:
logger.info('Attributes:')
for key, value in node.attributes.items():
logger.info(f' {key}: {value}')
#################################################
# SUMMARY STATISTICS
#################################################
# Get overall statistics about the knowledge graph
#################################################

logger.info('\n=== SUMMARY ===')
logger.info('Memgraph database populated successfully!')
logger.info('Knowledge graph is ready for queries and exploration.')

except Exception as e:
logger.error(f'An error occurred: {e}')
raise

finally:
#################################################
# CLEANUP
#################################################
# Always close the connection to Memgraph when
# finished to properly release resources
#################################################

# Close the connection
await graphiti.close()
logger.info('Connection closed.')


if __name__ == '__main__':
asyncio.run(main())
5 changes: 3 additions & 2 deletions graphiti_core/driver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
limitations under the License.
"""

from neo4j import Neo4jDriver
from .memgraph_driver import MemgraphDriver
from .neo4j_driver import Neo4jDriver

__all__ = ['Neo4jDriver']
__all__ = ['Neo4jDriver', 'MemgraphDriver']
Loading
Loading