This is page 1 of 12. Use http://codebase.md/getzep/graphiti?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .env.example
├── .github
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE
│ │ └── bug_report.md
│ ├── pull_request_template.md
│ ├── secret_scanning.yml
│ └── workflows
│ ├── ai-moderator.yml
│ ├── cla.yml
│ ├── claude-code-review-manual.yml
│ ├── claude-code-review.yml
│ ├── claude.yml
│ ├── codeql.yml
│ ├── daily_issue_maintenance.yml
│ ├── issue-triage.yml
│ ├── lint.yml
│ ├── release-graphiti-core.yml
│ ├── release-mcp-server.yml
│ ├── release-server-container.yml
│ ├── typecheck.yml
│ └── unit_tests.yml
├── .gitignore
├── AGENTS.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── conftest.py
├── CONTRIBUTING.md
├── depot.json
├── docker-compose.test.yml
├── docker-compose.yml
├── Dockerfile
├── ellipsis.yaml
├── examples
│ ├── azure-openai
│ │ ├── .env.example
│ │ ├── azure_openai_neo4j.py
│ │ └── README.md
│ ├── data
│ │ └── manybirds_products.json
│ ├── ecommerce
│ │ ├── runner.ipynb
│ │ └── runner.py
│ ├── langgraph-agent
│ │ ├── agent.ipynb
│ │ └── tinybirds-jess.png
│ ├── opentelemetry
│ │ ├── .env.example
│ │ ├── otel_stdout_example.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── podcast
│ │ ├── podcast_runner.py
│ │ ├── podcast_transcript.txt
│ │ └── transcript_parser.py
│ ├── quickstart
│ │ ├── quickstart_falkordb.py
│ │ ├── quickstart_neo4j.py
│ │ ├── quickstart_neptune.py
│ │ ├── README.md
│ │ └── requirements.txt
│ └── wizard_of_oz
│ ├── parser.py
│ ├── runner.py
│ └── woo.txt
├── graphiti_core
│ ├── __init__.py
│ ├── cross_encoder
│ │ ├── __init__.py
│ │ ├── bge_reranker_client.py
│ │ ├── client.py
│ │ ├── gemini_reranker_client.py
│ │ └── openai_reranker_client.py
│ ├── decorators.py
│ ├── driver
│ │ ├── __init__.py
│ │ ├── driver.py
│ │ ├── falkordb_driver.py
│ │ ├── graph_operations
│ │ │ └── graph_operations.py
│ │ ├── kuzu_driver.py
│ │ ├── neo4j_driver.py
│ │ ├── neptune_driver.py
│ │ └── search_interface
│ │ └── search_interface.py
│ ├── edges.py
│ ├── embedder
│ │ ├── __init__.py
│ │ ├── azure_openai.py
│ │ ├── client.py
│ │ ├── gemini.py
│ │ ├── openai.py
│ │ └── voyage.py
│ ├── errors.py
│ ├── graph_queries.py
│ ├── graphiti_types.py
│ ├── graphiti.py
│ ├── helpers.py
│ ├── llm_client
│ │ ├── __init__.py
│ │ ├── anthropic_client.py
│ │ ├── azure_openai_client.py
│ │ ├── client.py
│ │ ├── config.py
│ │ ├── errors.py
│ │ ├── gemini_client.py
│ │ ├── groq_client.py
│ │ ├── openai_base_client.py
│ │ ├── openai_client.py
│ │ ├── openai_generic_client.py
│ │ └── utils.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── edges
│ │ │ ├── __init__.py
│ │ │ └── edge_db_queries.py
│ │ └── nodes
│ │ ├── __init__.py
│ │ └── node_db_queries.py
│ ├── nodes.py
│ ├── prompts
│ │ ├── __init__.py
│ │ ├── dedupe_edges.py
│ │ ├── dedupe_nodes.py
│ │ ├── eval.py
│ │ ├── extract_edge_dates.py
│ │ ├── extract_edges.py
│ │ ├── extract_nodes.py
│ │ ├── invalidate_edges.py
│ │ ├── lib.py
│ │ ├── models.py
│ │ ├── prompt_helpers.py
│ │ ├── snippets.py
│ │ └── summarize_nodes.py
│ ├── py.typed
│ ├── search
│ │ ├── __init__.py
│ │ ├── search_config_recipes.py
│ │ ├── search_config.py
│ │ ├── search_filters.py
│ │ ├── search_helpers.py
│ │ ├── search_utils.py
│ │ └── search.py
│ ├── telemetry
│ │ ├── __init__.py
│ │ └── telemetry.py
│ ├── tracer.py
│ └── utils
│ ├── __init__.py
│ ├── bulk_utils.py
│ ├── datetime_utils.py
│ ├── maintenance
│ │ ├── __init__.py
│ │ ├── community_operations.py
│ │ ├── dedup_helpers.py
│ │ ├── edge_operations.py
│ │ ├── graph_data_operations.py
│ │ ├── node_operations.py
│ │ └── temporal_operations.py
│ ├── ontology_utils
│ │ └── entity_types_utils.py
│ └── text_utils.py
├── images
│ ├── arxiv-screenshot.png
│ ├── graphiti-graph-intro.gif
│ ├── graphiti-intro-slides-stock-2.gif
│ └── simple_graph.svg
├── LICENSE
├── Makefile
├── mcp_server
│ ├── .env.example
│ ├── .python-version
│ ├── config
│ │ ├── config-docker-falkordb-combined.yaml
│ │ ├── config-docker-falkordb.yaml
│ │ ├── config-docker-neo4j.yaml
│ │ ├── config.yaml
│ │ └── mcp_config_stdio_example.json
│ ├── docker
│ │ ├── build-standalone.sh
│ │ ├── build-with-version.sh
│ │ ├── docker-compose-falkordb.yml
│ │ ├── docker-compose-neo4j.yml
│ │ ├── docker-compose.yml
│ │ ├── Dockerfile
│ │ ├── Dockerfile.standalone
│ │ ├── github-actions-example.yml
│ │ ├── README-falkordb-combined.md
│ │ └── README.md
│ ├── docs
│ │ └── cursor_rules.md
│ ├── main.py
│ ├── pyproject.toml
│ ├── pytest.ini
│ ├── README.md
│ ├── src
│ │ ├── __init__.py
│ │ ├── config
│ │ │ ├── __init__.py
│ │ │ └── schema.py
│ │ ├── graphiti_mcp_server.py
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ ├── entity_types.py
│ │ │ └── response_types.py
│ │ ├── services
│ │ │ ├── __init__.py
│ │ │ ├── factories.py
│ │ │ └── queue_service.py
│ │ └── utils
│ │ ├── __init__.py
│ │ ├── formatting.py
│ │ └── utils.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── pytest.ini
│ │ ├── README.md
│ │ ├── run_tests.py
│ │ ├── test_async_operations.py
│ │ ├── test_comprehensive_integration.py
│ │ ├── test_configuration.py
│ │ ├── test_falkordb_integration.py
│ │ ├── test_fixtures.py
│ │ ├── test_http_integration.py
│ │ ├── test_integration.py
│ │ ├── test_mcp_integration.py
│ │ ├── test_mcp_transports.py
│ │ ├── test_stdio_simple.py
│ │ └── test_stress_load.py
│ └── uv.lock
├── OTEL_TRACING.md
├── py.typed
├── pyproject.toml
├── pytest.ini
├── README.md
├── SECURITY.md
├── server
│ ├── .env.example
│ ├── graph_service
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── dto
│ │ │ ├── __init__.py
│ │ │ ├── common.py
│ │ │ ├── ingest.py
│ │ │ └── retrieve.py
│ │ ├── main.py
│ │ ├── routers
│ │ │ ├── __init__.py
│ │ │ ├── ingest.py
│ │ │ └── retrieve.py
│ │ └── zep_graphiti.py
│ ├── Makefile
│ ├── pyproject.toml
│ ├── README.md
│ └── uv.lock
├── signatures
│ └── version1
│ └── cla.json
├── tests
│ ├── cross_encoder
│ │ ├── test_bge_reranker_client_int.py
│ │ └── test_gemini_reranker_client.py
│ ├── driver
│ │ ├── __init__.py
│ │ └── test_falkordb_driver.py
│ ├── embedder
│ │ ├── embedder_fixtures.py
│ │ ├── test_gemini.py
│ │ ├── test_openai.py
│ │ └── test_voyage.py
│ ├── evals
│ │ ├── data
│ │ │ └── longmemeval_data
│ │ │ ├── longmemeval_oracle.json
│ │ │ └── README.md
│ │ ├── eval_cli.py
│ │ ├── eval_e2e_graph_building.py
│ │ ├── pytest.ini
│ │ └── utils.py
│ ├── helpers_test.py
│ ├── llm_client
│ │ ├── test_anthropic_client_int.py
│ │ ├── test_anthropic_client.py
│ │ ├── test_azure_openai_client.py
│ │ ├── test_client.py
│ │ ├── test_errors.py
│ │ └── test_gemini_client.py
│ ├── test_edge_int.py
│ ├── test_entity_exclusion_int.py
│ ├── test_graphiti_int.py
│ ├── test_graphiti_mock.py
│ ├── test_node_int.py
│ ├── test_text_utils.py
│ └── utils
│ ├── maintenance
│ │ ├── test_bulk_utils.py
│ │ ├── test_edge_operations.py
│ │ ├── test_node_operations.py
│ │ └── test_temporal_operations_int.py
│ └── search
│ └── search_utils_test.py
├── uv.lock
└── Zep-CLA.md
```
# Files
--------------------------------------------------------------------------------
/mcp_server/.python-version:
--------------------------------------------------------------------------------
```
1 | 3.10
2 |
```
--------------------------------------------------------------------------------
/examples/opentelemetry/.env.example:
--------------------------------------------------------------------------------
```
1 | # OpenAI API key (required for LLM inference and embeddings)
2 | OPENAI_API_KEY=your_api_key_here
3 |
```
--------------------------------------------------------------------------------
/server/.env.example:
--------------------------------------------------------------------------------
```
1 | OPENAI_API_KEY=
2 | NEO4J_PORT=7687
3 | # Only used if not running a neo4j container in docker
4 | NEO4J_URI=bolt://localhost:7687
5 | NEO4J_USER=neo4j
6 | NEO4J_PASSWORD=password
```
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
```
1 | OPENAI_API_KEY=
2 |
3 | # Neo4j database connection
4 | NEO4J_URI=
5 | NEO4J_PORT=
6 | NEO4J_USER=
7 | NEO4J_PASSWORD=
8 |
9 | # FalkorDB database connection
10 | FALKORDB_URI=
11 | FALKORDB_PORT=
12 | FALKORDB_USER=
13 | FALKORDB_PASSWORD=
14 |
15 | USE_PARALLEL_RUNTIME=
16 | SEMAPHORE_LIMIT=
17 | GITHUB_SHA=
18 | MAX_REFLEXION_ITERATIONS=
19 | ANTHROPIC_API_KEY=
```
--------------------------------------------------------------------------------
/examples/azure-openai/.env.example:
--------------------------------------------------------------------------------
```
1 | # Neo4j connection settings
2 | NEO4J_URI=bolt://localhost:7687
3 | NEO4J_USER=neo4j
4 | NEO4J_PASSWORD=password
5 |
6 | # Azure OpenAI settings
7 | AZURE_OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com
8 | AZURE_OPENAI_API_KEY=your-api-key-here
9 | AZURE_OPENAI_DEPLOYMENT=gpt-5-mini
10 | AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
11 |
```
--------------------------------------------------------------------------------
/mcp_server/.env.example:
--------------------------------------------------------------------------------
```
1 | # Graphiti MCP Server Environment Configuration
2 |
3 | # Neo4j Database Configuration
4 | # These settings are used to connect to your Neo4j database
5 | NEO4J_URI=bolt://localhost:7687
6 | NEO4J_USER=neo4j
7 | NEO4J_PASSWORD=demodemo
8 |
9 | # OpenAI API Configuration
10 | # Required for LLM operations
11 | OPENAI_API_KEY=your_openai_api_key_here
12 | MODEL_NAME=gpt-4.1-mini
13 |
14 | # Optional: Only needed for non-standard OpenAI endpoints
15 | # OPENAI_BASE_URL=https://api.openai.com/v1
16 |
17 | # Optional: Group ID for namespacing graph data
18 | # GROUP_ID=my_project
19 |
20 | # Concurrency Control
21 | # Controls how many episodes can be processed simultaneously
22 | # Default: 10 (suitable for OpenAI Tier 3, mid-tier Anthropic)
23 | # Adjust based on your LLM provider's rate limits:
24 | # - OpenAI Tier 1 (free): 1-2
25 | # - OpenAI Tier 2: 5-8
26 | # - OpenAI Tier 3: 10-15
27 | # - OpenAI Tier 4: 20-50
28 | # - Anthropic default: 5-8
29 | # - Anthropic high tier: 15-30
30 | # - Ollama (local): 1-5
31 | # See README.md "Concurrency and LLM Provider 429 Rate Limit Errors" for details
32 | SEMAPHORE_LIMIT=10
33 |
34 | # Optional: Path configuration for Docker
35 | # PATH=/root/.local/bin:${PATH}
36 |
37 | # Optional: Memory settings for Neo4j (used in Docker Compose)
38 | # NEO4J_server_memory_heap_initial__size=512m
39 | # NEO4J_server_memory_heap_max__size=1G
40 | # NEO4J_server_memory_pagecache_size=512m
41 |
42 | # Azure OpenAI configuration
43 | # Optional: Only needed for Azure OpenAI endpoints
44 | # AZURE_OPENAI_ENDPOINT=your_azure_openai_endpoint_here
45 | # AZURE_OPENAI_API_VERSION=2025-01-01-preview
46 | # AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-gpt-4o-mini-deployment
47 | # AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15
48 | # AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-3-large-deployment
49 | # AZURE_OPENAI_USE_MANAGED_IDENTITY=false
50 |
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | ### Python template
2 | # Byte-compiled / optimized / DLL files
3 | __pycache__/
4 | *.py[cod]
5 | *$py.class
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 | cover/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | .pybuilder/
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 |
82 | # IPython
83 | profile_default/
84 | ipython_config.py
85 |
86 | # pyenv
87 | # For a library or package, you might want to ignore these files since the code is
88 | # intended to run in multiple environments; otherwise, check them in:
89 | # .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # uv
99 | # It is generally recommended to include uv.lock in version control.
100 | # This ensures reproducibility across different environments.
101 | # https://docs.astral.sh/uv/concepts/projects/#lockfile
102 | # uv.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | .idea/
161 | .vscode/
162 |
163 | ## Other
164 | # Cache files
165 | cache.db*
166 |
167 | # All DS_Store files
168 | .DS_Store
```
--------------------------------------------------------------------------------
/tests/evals/data/longmemeval_data/README.md:
--------------------------------------------------------------------------------
```markdown
1 | The `longmemeval_oracle` dataset is an open-source dataset that we are using.
2 | We did not create this dataset and it can be found
3 | here: https://huggingface.co/datasets/xiaowu0162/longmemeval/blob/main/longmemeval_oracle.
4 |
```
--------------------------------------------------------------------------------
/examples/opentelemetry/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # OpenTelemetry Stdout Tracing Example
2 |
3 | Configure Graphiti with OpenTelemetry to output trace spans to stdout.
4 |
5 | ## Setup
6 |
7 | ```bash
8 | uv sync
9 | export OPENAI_API_KEY=your_api_key_here
10 | uv run otel_stdout_example.py
11 | ```
12 |
13 | ## Configure OpenTelemetry with Graphiti
14 |
15 | ```python
16 | from opentelemetry import trace
17 | from opentelemetry.sdk.trace import TracerProvider
18 | from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
19 |
20 | # Set up OpenTelemetry with stdout exporter
21 | provider = TracerProvider()
22 | provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
23 | trace.set_tracer_provider(provider)
24 |
25 | # Get tracer and pass to Graphiti
26 | tracer = trace.get_tracer(__name__)
27 | graphiti = Graphiti(
28 | graph_driver=kuzu_driver,
29 | tracer=tracer,
30 | trace_span_prefix='graphiti.example'
31 | )
32 | ```
33 |
```
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # graph-service
2 |
3 | Graph service is a fast api server implementing the [graphiti](https://github.com/getzep/graphiti) package.
4 |
5 | ## Container Releases
6 |
7 | The FastAPI server container is automatically built and published to Docker Hub when a new `graphiti-core` version is released to PyPI.
8 |
9 | **Image:** `zepai/graphiti`
10 |
11 | **Available tags:**
12 | - `latest` - Latest stable release
13 | - `0.22.1` - Specific version (matches graphiti-core version)
14 |
15 | **Platforms:** linux/amd64, linux/arm64
16 |
17 | The automated release workflow:
18 | 1. Triggers when `graphiti-core` PyPI release completes
19 | 2. Waits for PyPI package availability
20 | 3. Builds multi-platform Docker image
21 | 4. Tags with version number and `latest`
22 | 5. Pushes to Docker Hub
23 |
24 | Only stable releases are built automatically (pre-release versions are skipped).
25 |
26 | ## Running Instructions
27 |
28 | 1. Ensure you have Docker and Docker Compose installed on your system.
29 |
30 | 2. Add `zepai/graphiti:latest` to your service setup
31 |
32 | 3. Make sure to pass the following environment variables to the service
33 |
34 | ```
35 | OPENAI_API_KEY=your_openai_api_key
36 | NEO4J_USER=your_neo4j_user
37 | NEO4J_PASSWORD=your_neo4j_password
38 | NEO4J_PORT=your_neo4j_port
39 | ```
40 |
41 | 4. This service depends on having access to a neo4j instance, you may wish to add a neo4j image to your service setup as well. Or you may wish to use neo4j cloud or a desktop version if running this locally.
42 |
43 | An example of docker compose setup may look like this:
44 |
45 | ```yml
46 | version: '3.8'
47 |
48 | services:
49 | graph:
50 | image: zepai/graphiti:latest
51 | ports:
52 | - "8000:8000"
53 |
54 | environment:
55 | - OPENAI_API_KEY=${OPENAI_API_KEY}
56 | - NEO4J_URI=bolt://neo4j:${NEO4J_PORT}
57 | - NEO4J_USER=${NEO4J_USER}
58 | - NEO4J_PASSWORD=${NEO4J_PASSWORD}
59 | neo4j:
60 | image: neo4j:5.22.0
61 |
62 | ports:
63 | - "7474:7474" # HTTP
64 | - "${NEO4J_PORT}:${NEO4J_PORT}" # Bolt
65 | volumes:
66 | - neo4j_data:/data
67 | environment:
68 | - NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}
69 |
70 | volumes:
71 | neo4j_data:
72 | ```
73 |
74 | 5. Once you start the service, it will be available at `http://localhost:8000` (or the port you have specified in the docker compose file).
75 |
76 | 6. You may access the swagger docs at `http://localhost:8000/docs`. You may also access redocs at `http://localhost:8000/redoc`.
77 |
78 | 7. You may also access the neo4j browser at `http://localhost:7474` (the port depends on the neo4j instance you are using).
```
--------------------------------------------------------------------------------
/examples/azure-openai/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Azure OpenAI with Neo4j Example
2 |
3 | This example demonstrates how to use Graphiti with Azure OpenAI and Neo4j to build a knowledge graph.
4 |
5 | ## Prerequisites
6 |
7 | - Python 3.10+
8 | - Neo4j database (running locally or remotely)
9 | - Azure OpenAI subscription with deployed models
10 |
11 | ## Setup
12 |
13 | ### 1. Install Dependencies
14 |
15 | ```bash
16 | uv sync
17 | ```
18 |
19 | ### 2. Configure Environment Variables
20 |
21 | Copy the `.env.example` file to `.env` and fill in your credentials:
22 |
23 | ```bash
24 | cd examples/azure-openai
25 | cp .env.example .env
26 | ```
27 |
28 | Edit `.env` with your actual values:
29 |
30 | ```env
31 | # Neo4j connection settings
32 | NEO4J_URI=bolt://localhost:7687
33 | NEO4J_USER=neo4j
34 | NEO4J_PASSWORD=your-password
35 |
36 | # Azure OpenAI settings
37 | AZURE_OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com
38 | AZURE_OPENAI_API_KEY=your-api-key-here
39 | AZURE_OPENAI_DEPLOYMENT=gpt-5-mini
40 | AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
41 | ```
42 |
43 | ### 3. Azure OpenAI Model Deployments
44 |
45 | This example requires two Azure OpenAI model deployments:
46 |
47 | 1. **Chat Completion Model**: Used for entity extraction and relationship analysis
48 | - Set the deployment name in `AZURE_OPENAI_DEPLOYMENT`
49 |
50 | 2. **Embedding Model**: Used for semantic search
51 | - Set the deployment name in `AZURE_OPENAI_EMBEDDING_DEPLOYMENT`
52 |
53 | ### 4. Neo4j Setup
54 |
55 | Make sure Neo4j is running and accessible at the URI specified in your `.env` file.
56 |
57 | For local development:
58 | - Download and install [Neo4j Desktop](https://neo4j.com/download/)
59 | - Create a new database
60 | - Start the database
61 | - Use the credentials in your `.env` file
62 |
63 | ## Running the Example
64 |
65 | ```bash
66 | cd examples/azure-openai
67 | uv run azure_openai_neo4j.py
68 | ```
69 |
70 | ## What This Example Does
71 |
72 | 1. **Initialization**: Sets up connections to Neo4j and Azure OpenAI
73 | 2. **Adding Episodes**: Ingests text and JSON data about California politics
74 | 3. **Basic Search**: Performs hybrid search combining semantic similarity and BM25 retrieval
75 | 4. **Center Node Search**: Reranks results based on graph distance to a specific node
76 | 5. **Cleanup**: Properly closes database connections
77 |
78 | ## Key Concepts
79 |
80 | ### Azure OpenAI Integration
81 |
82 | The example shows how to configure Graphiti to use Azure OpenAI with the OpenAI v1 API:
83 |
84 | ```python
85 | # Initialize Azure OpenAI client using the standard OpenAI client
86 | # with Azure's v1 API endpoint
87 | azure_client = AsyncOpenAI(
88 | base_url=f"{azure_endpoint}/openai/v1/",
89 | api_key=azure_api_key,
90 | )
91 |
92 | # Create LLM and Embedder clients
93 | llm_client = AzureOpenAILLMClient(
94 | azure_client=azure_client,
95 | config=LLMConfig(model=azure_deployment, small_model=azure_deployment)
96 | )
97 | embedder_client = AzureOpenAIEmbedderClient(
98 | azure_client=azure_client,
99 | model=azure_embedding_deployment
100 | )
101 |
102 | # Initialize Graphiti with custom clients
103 | graphiti = Graphiti(
104 | neo4j_uri,
105 | neo4j_user,
106 | neo4j_password,
107 | llm_client=llm_client,
108 | embedder=embedder_client,
109 | )
110 | ```
111 |
112 | **Note**: This example uses Azure OpenAI's v1 API compatibility layer, which allows using the standard `AsyncOpenAI` client. The endpoint format is `https://your-resource-name.openai.azure.com/openai/v1/`.
113 |
114 | ### Episodes
115 |
116 | Episodes are the primary units of information in Graphiti. They can be:
117 | - **Text**: Raw text content (e.g., transcripts, documents)
118 | - **JSON**: Structured data with key-value pairs
119 |
120 | ### Hybrid Search
121 |
122 | Graphiti combines multiple search strategies:
123 | - **Semantic Search**: Uses embeddings to find semantically similar content
124 | - **BM25**: Keyword-based text retrieval
125 | - **Graph Traversal**: Leverages relationships between entities
126 |
127 | ## Troubleshooting
128 |
129 | ### Azure OpenAI API Errors
130 |
131 | - Verify your endpoint URL is correct (should end in `.openai.azure.com`)
132 | - Check that your API key is valid
133 | - Ensure your deployment names match actual deployments in Azure
134 | - Verify API version is supported by your deployment
135 |
136 | ### Neo4j Connection Issues
137 |
138 | - Ensure Neo4j is running
139 | - Check firewall settings
140 | - Verify credentials are correct
141 | - Check URI format (should be `bolt://` or `neo4j://`)
142 |
143 | ## Next Steps
144 |
145 | - Explore other search recipes in `graphiti_core/search/search_config_recipes.py`
146 | - Try different episode types and content
147 | - Experiment with custom entity definitions
148 | - Add more episodes to build a larger knowledge graph
149 |
150 | ## Related Examples
151 |
152 | - `examples/quickstart/` - Basic Graphiti usage with OpenAI
153 | - `examples/podcast/` - Processing longer content
154 | - `examples/ecommerce/` - Domain-specific knowledge graphs
155 |
```
--------------------------------------------------------------------------------
/examples/quickstart/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Graphiti Quickstart Example
2 |
3 | This example demonstrates the basic functionality of Graphiti, including:
4 |
5 | 1. Connecting to a Neo4j or FalkorDB database
6 | 2. Initializing Graphiti indices and constraints
7 | 3. Adding episodes to the graph
8 | 4. Searching the graph with semantic and keyword matching
9 | 5. Exploring graph-based search with reranking using the top search result's source node UUID
10 | 6. Performing node search using predefined search recipes
11 |
12 | ## Prerequisites
13 |
14 | - Python 3.9+
15 | - OpenAI API key (set as `OPENAI_API_KEY` environment variable)
16 | - **For Neo4j**:
17 | - Neo4j Desktop installed and running
18 | - A local DBMS created and started in Neo4j Desktop
19 | - **For FalkorDB**:
20 | - FalkorDB server running (see [FalkorDB documentation](https://docs.falkordb.com) for setup)
21 | - **For Amazon Neptune**:
22 | - Amazon server running (see [Amazon Neptune documentation](https://aws.amazon.com/neptune/developer-resources/) for setup)
23 |
24 |
25 | ## Setup Instructions
26 |
27 | 1. Install the required dependencies:
28 |
29 | ```bash
30 | pip install graphiti-core
31 | ```
32 |
33 | 2. Set up environment variables:
34 |
35 | ```bash
36 | # Required for LLM and embedding
37 | export OPENAI_API_KEY=your_openai_api_key
38 |
39 | # Optional Neo4j connection parameters (defaults shown)
40 | export NEO4J_URI=bolt://localhost:7687
41 | export NEO4J_USER=neo4j
42 | export NEO4J_PASSWORD=password
43 |
44 | # Optional FalkorDB connection parameters (defaults shown)
45 | export FALKORDB_URI=falkor://localhost:6379
46 |
47 | # Optional Amazon Neptune connection parameters
48 | NEPTUNE_HOST=your_neptune_host
49 | NEPTUNE_PORT=your_port_or_8182
50 | AOSS_HOST=your_aoss_host
51 | AOSS_PORT=your_port_or_443
52 |
53 | # To use a different database, modify the driver constructor in the script
54 | ```
55 |
56 | TIP: For Amazon Neptune host string please use the following formats
57 | * For Neptune Database: `neptune-db://<cluster endpoint>`
58 | * For Neptune Analytics: `neptune-graph://<graph identifier>`
59 |
60 | 3. Run the example:
61 |
62 | ```bash
63 | python quickstart_neo4j.py
64 |
65 | # For FalkorDB
66 | python quickstart_falkordb.py
67 |
68 | # For Amazon Neptune
69 | python quickstart_neptune.py
70 | ```
71 |
72 | ## What This Example Demonstrates
73 |
74 | - **Graph Initialization**: Setting up the Graphiti indices and constraints in Neo4j, Amazon Neptune, or FalkorDB
75 | - **Adding Episodes**: Adding text content that will be analyzed and converted into knowledge graph nodes and edges
76 | - **Edge Search Functionality**: Performing hybrid searches that combine semantic similarity and BM25 retrieval to find relationships (edges)
77 | - **Graph-Aware Search**: Using the source node UUID from the top search result to rerank additional search results based on graph distance
78 | - **Node Search Using Recipes**: Using predefined search configurations like NODE_HYBRID_SEARCH_RRF to directly search for nodes rather than edges
79 | - **Result Processing**: Understanding the structure of search results including facts, nodes, and temporal metadata
80 |
81 | ## Next Steps
82 |
83 | After running this example, you can:
84 |
85 | 1. Modify the episode content to add your own information
86 | 2. Try different search queries to explore the knowledge extraction
87 | 3. Experiment with different center nodes for graph-distance-based reranking
88 | 4. Try other predefined search recipes from `graphiti_core.search.search_config_recipes`
89 | 5. Explore the more advanced examples in the other directories
90 |
91 | ## Troubleshooting
92 |
93 | ### "Graph not found: default_db" Error
94 |
95 | If you encounter the error `Neo.ClientError.Database.DatabaseNotFound: Graph not found: default_db`, this occurs when the driver is trying to connect to a database that doesn't exist.
96 |
97 | **Solution:**
98 | The Neo4j driver defaults to using `neo4j` as the database name. If you need to use a different database, modify the driver constructor in the script:
99 |
100 | ```python
101 | # In quickstart_neo4j.py, change:
102 | driver = Neo4jDriver(uri=neo4j_uri, user=neo4j_user, password=neo4j_password)
103 |
104 | # To specify a different database:
105 | driver = Neo4jDriver(uri=neo4j_uri, user=neo4j_user, password=neo4j_password, database="your_db_name")
106 | ```
107 |
108 | ## Understanding the Output
109 |
110 | ### Edge Search Results
111 |
112 | The edge search results include EntityEdge objects with:
113 |
114 | - UUID: Unique identifier for the edge
115 | - Fact: The extracted fact from the episode
116 | - Valid at/invalid at: Time period during which the fact was true (if available)
117 | - Source/target node UUIDs: Connections between entities in the knowledge graph
118 |
119 | ### Node Search Results
120 |
121 | The node search results include EntityNode objects with:
122 |
123 | - UUID: Unique identifier for the node
124 | - Name: The name of the entity
125 | - Content Summary: A summary of the node's content
126 | - Node Labels: The types of the node (e.g., Person, Organization)
127 | - Created At: When the node was created
128 | - Attributes: Additional properties associated with the node
129 |
```
--------------------------------------------------------------------------------
/mcp_server/docker/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Docker Deployment for Graphiti MCP Server
2 |
3 | This directory contains Docker Compose configurations for running the Graphiti MCP server with graph database backends: FalkorDB (combined image) and Neo4j.
4 |
5 | ## Quick Start
6 |
7 | ```bash
8 | # Default configuration (FalkorDB combined image)
9 | docker-compose up
10 |
11 | # Neo4j (separate containers)
12 | docker-compose -f docker-compose-neo4j.yml up
13 | ```
14 |
15 | ## Environment Variables
16 |
17 | Create a `.env` file in this directory with your API keys:
18 |
19 | ```bash
20 | # Required
21 | OPENAI_API_KEY=your-api-key-here
22 |
23 | # Optional
24 | GRAPHITI_GROUP_ID=main
25 | SEMAPHORE_LIMIT=10
26 |
27 | # Database-specific variables (see database sections below)
28 | ```
29 |
30 | ## Database Configurations
31 |
32 | ### FalkorDB (Combined Image)
33 |
34 | **File:** `docker-compose.yml` (default)
35 |
36 | The default configuration uses a combined Docker image that bundles both FalkorDB and the MCP server together for simplified deployment.
37 |
38 | #### Configuration
39 |
40 | ```bash
41 | # Environment variables
42 | FALKORDB_URI=redis://localhost:6379 # Connection URI (services run in same container)
43 | FALKORDB_PASSWORD= # Password (default: empty)
44 | FALKORDB_DATABASE=default_db # Database name (default: default_db)
45 | ```
46 |
47 | #### Accessing Services
48 |
49 | - **FalkorDB (Redis):** redis://localhost:6379
50 | - **FalkorDB Web UI:** http://localhost:3000
51 | - **MCP Server:** http://localhost:8000
52 |
53 | #### Data Management
54 |
55 | **Backup:**
56 | ```bash
57 | docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \
58 | tar czf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data .
59 | ```
60 |
61 | **Restore:**
62 | ```bash
63 | docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \
64 | tar xzf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data
65 | ```
66 |
67 | **Clear Data:**
68 | ```bash
69 | docker-compose down
70 | docker volume rm mcp_server_falkordb_data
71 | docker-compose up
72 | ```
73 |
74 | #### Gotchas
75 | - Both FalkorDB and MCP server run in the same container
76 | - FalkorDB uses Redis persistence mechanisms (AOF/RDB)
77 | - Default configuration has no password - add one for production
78 | - Health check only monitors FalkorDB; MCP server startup visible in logs
79 |
80 | See [README-falkordb-combined.md](README-falkordb-combined.md) for detailed information about the combined image.
81 |
82 | ### Neo4j
83 |
84 | **File:** `docker-compose-neo4j.yml`
85 |
86 | Neo4j runs as a separate container service with its own web interface.
87 |
88 | #### Configuration
89 |
90 | ```bash
91 | # Environment variables
92 | NEO4J_URI=bolt://neo4j:7687 # Connection URI (default: bolt://neo4j:7687)
93 | NEO4J_USER=neo4j # Username (default: neo4j)
94 | NEO4J_PASSWORD=demodemo # Password (default: demodemo)
95 | NEO4J_DATABASE=neo4j # Database name (default: neo4j)
96 | USE_PARALLEL_RUNTIME=false # Enterprise feature (default: false)
97 | ```
98 |
99 | #### Accessing Neo4j
100 |
101 | - **Web Interface:** http://localhost:7474
102 | - **Bolt Protocol:** bolt://localhost:7687
103 | - **MCP Server:** http://localhost:8000
104 |
105 | Default credentials: `neo4j` / `demodemo`
106 |
107 | #### Data Management
108 |
109 | **Backup:**
110 | ```bash
111 | # Backup both data and logs volumes
112 | docker run --rm -v docker_neo4j_data:/data -v $(pwd):/backup alpine \
113 | tar czf /backup/neo4j-data-backup.tar.gz -C /data .
114 | docker run --rm -v docker_neo4j_logs:/logs -v $(pwd):/backup alpine \
115 | tar czf /backup/neo4j-logs-backup.tar.gz -C /logs .
116 | ```
117 |
118 | **Restore:**
119 | ```bash
120 | # Restore both volumes
121 | docker run --rm -v docker_neo4j_data:/data -v $(pwd):/backup alpine \
122 | tar xzf /backup/neo4j-data-backup.tar.gz -C /data
123 | docker run --rm -v docker_neo4j_logs:/logs -v $(pwd):/backup alpine \
124 | tar xzf /backup/neo4j-logs-backup.tar.gz -C /logs
125 | ```
126 |
127 | **Clear Data:**
128 | ```bash
129 | docker-compose -f docker-compose-neo4j.yml down
130 | docker volume rm docker_neo4j_data docker_neo4j_logs
131 | docker-compose -f docker-compose-neo4j.yml up
132 | ```
133 |
134 | #### Gotchas
135 | - Neo4j takes 30+ seconds to start up - wait for the health check
136 | - The web interface requires authentication even for local access
137 | - Memory heap is configured for 512MB initial, 1GB max
138 | - Page cache is set to 512MB
139 | - Enterprise features like parallel runtime require a license
140 |
141 | ## Switching Between Databases
142 |
143 | To switch from FalkorDB to Neo4j (or vice versa):
144 |
145 | 1. **Stop current setup:**
146 | ```bash
147 | docker-compose down # Stop FalkorDB combined image
148 | # or
149 | docker-compose -f docker-compose-neo4j.yml down # Stop Neo4j
150 | ```
151 |
152 | 2. **Start new database:**
153 | ```bash
154 | docker-compose up # Start FalkorDB combined image
155 | # or
156 | docker-compose -f docker-compose-neo4j.yml up # Start Neo4j
157 | ```
158 |
159 | Note: Data is not automatically migrated between different database types. You'll need to export from one and import to another using the MCP API.
160 |
161 | ## Troubleshooting
162 |
163 | ### Port Conflicts
164 |
165 | If port 8000 is already in use:
166 | ```bash
167 | # Find what's using the port
168 | lsof -i :8000
169 |
170 | # Change the port in docker-compose.yml
171 | # Under ports section: "8001:8000"
172 | ```
173 |
174 | ### Container Won't Start
175 |
176 | 1. Check logs:
177 | ```bash
178 | docker-compose logs graphiti-mcp
179 | ```
180 |
181 | 2. Verify `.env` file exists and contains valid API keys:
182 | ```bash
183 | cat .env | grep API_KEY
184 | ```
185 |
186 | 3. Ensure Docker has enough resources allocated
187 |
188 | ### Database Connection Issues
189 |
190 | **FalkorDB:**
191 | - Test Redis connectivity: `docker compose exec graphiti-falkordb redis-cli ping`
192 | - Check FalkorDB logs: `docker compose logs graphiti-falkordb`
193 | - Verify both services started: Look for "FalkorDB is ready!" and "Starting MCP server..." in logs
194 |
195 | **Neo4j:**
196 | - Wait for health check to pass (can take 30+ seconds)
197 | - Check Neo4j logs: `docker-compose -f docker-compose-neo4j.yml logs neo4j`
198 | - Verify credentials match environment variables
199 |
200 | **FalkorDB:**
201 | - Test Redis connectivity: `redis-cli -h localhost ping`
202 |
203 | ### Data Not Persisting
204 |
205 | 1. Verify volumes are created:
206 | ```bash
207 | docker volume ls | grep docker_
208 | ```
209 |
210 | 2. Check volume mounts in container:
211 | ```bash
212 | docker inspect graphiti-mcp | grep -A 5 Mounts
213 | ```
214 |
215 | 3. Ensure proper shutdown:
216 | ```bash
217 | docker-compose down # Not docker-compose down -v (which removes volumes)
218 | ```
219 |
220 | ### Performance Issues
221 |
222 | **FalkorDB:**
223 | - Adjust `SEMAPHORE_LIMIT` environment variable
224 | - Monitor with: `docker stats graphiti-falkordb`
225 | - Check Redis memory: `docker compose exec graphiti-falkordb redis-cli info memory`
226 |
227 | **Neo4j:**
228 | - Increase heap memory in docker-compose-neo4j.yml
229 | - Adjust page cache size based on data size
230 | - Check query performance in Neo4j browser
231 |
232 | ## Docker Resources
233 |
234 | ### Volumes
235 |
236 | Each database configuration uses named volumes for data persistence:
237 | - FalkorDB (combined): `falkordb_data`
238 | - Neo4j: `neo4j_data`, `neo4j_logs`
239 |
240 | ### Networks
241 |
242 | All configurations use the default bridge network. Services communicate using container names as hostnames.
243 |
244 | ### Resource Limits
245 |
246 | No resource limits are set by default. To add limits, modify the docker-compose file:
247 |
248 | ```yaml
249 | services:
250 | graphiti-mcp:
251 | deploy:
252 | resources:
253 | limits:
254 | cpus: '2.0'
255 | memory: 1G
256 | ```
257 |
258 | ## Configuration Files
259 |
260 | Each database has a dedicated configuration file in `../config/`:
261 | - `config-docker-falkordb-combined.yaml` - FalkorDB combined image configuration
262 | - `config-docker-neo4j.yaml` - Neo4j configuration
263 |
264 | These files are mounted read-only into the container at `/app/mcp/config/config.yaml` (for combined image) or `/app/config/config.yaml` (for Neo4j).
```
--------------------------------------------------------------------------------
/mcp_server/tests/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Graphiti MCP Server Integration Tests
2 |
3 | This directory contains a comprehensive integration test suite for the Graphiti MCP Server using the official Python MCP SDK.
4 |
5 | ## Overview
6 |
7 | The test suite is designed to thoroughly test all aspects of the Graphiti MCP server with special consideration for LLM inference latency and system performance.
8 |
9 | ## Test Organization
10 |
11 | ### Core Test Modules
12 |
13 | - **`test_comprehensive_integration.py`** - Main integration test suite covering all MCP tools
14 | - **`test_async_operations.py`** - Tests for concurrent operations and async patterns
15 | - **`test_stress_load.py`** - Stress testing and load testing scenarios
16 | - **`test_fixtures.py`** - Shared fixtures and test utilities
17 | - **`test_mcp_integration.py`** - Original MCP integration tests
18 | - **`test_configuration.py`** - Configuration loading and validation tests
19 |
20 | ### Test Categories
21 |
22 | Tests are organized with pytest markers:
23 |
24 | - `unit` - Fast unit tests without external dependencies
25 | - `integration` - Tests requiring database and services
26 | - `slow` - Long-running tests (stress/load tests)
27 | - `requires_neo4j` - Tests requiring Neo4j
28 | - `requires_falkordb` - Tests requiring FalkorDB
29 | - `requires_openai` - Tests requiring OpenAI API key
30 |
31 | ## Installation
32 |
33 | ```bash
34 | # Install test dependencies
35 | uv add --dev pytest pytest-asyncio pytest-timeout pytest-xdist faker psutil
36 |
37 | # Install MCP SDK
38 | uv add mcp
39 | ```
40 |
41 | ## Running Tests
42 |
43 | ### Quick Start
44 |
45 | ```bash
46 | # Run smoke tests (quick validation)
47 | python tests/run_tests.py smoke
48 |
49 | # Run integration tests with mock LLM
50 | python tests/run_tests.py integration --mock-llm
51 |
52 | # Run all tests
53 | python tests/run_tests.py all
54 | ```
55 |
56 | ### Test Runner Options
57 |
58 | ```bash
59 | python tests/run_tests.py [suite] [options]
60 |
61 | Suites:
62 | unit - Unit tests only
63 | integration - Integration tests
64 | comprehensive - Comprehensive integration suite
65 | async - Async operation tests
66 | stress - Stress and load tests
67 | smoke - Quick smoke tests
68 | all - All tests
69 |
70 | Options:
71 | --database - Database backend (neo4j, falkordb)
72 | --mock-llm - Use mock LLM for faster testing
73 | --parallel N - Run tests in parallel with N workers
74 | --coverage - Generate coverage report
75 | --skip-slow - Skip slow tests
76 | --timeout N - Test timeout in seconds
77 | --check-only - Only check prerequisites
78 | ```
79 |
80 | ### Examples
81 |
82 | ```bash
83 | # Quick smoke test with FalkorDB (default)
84 | python tests/run_tests.py smoke
85 |
86 | # Full integration test with Neo4j
87 | python tests/run_tests.py integration --database neo4j
88 |
89 | # Stress testing with parallel execution
90 | python tests/run_tests.py stress --parallel 4
91 |
92 | # Run with coverage
93 | python tests/run_tests.py all --coverage
94 |
95 | # Check prerequisites only
96 | python tests/run_tests.py all --check-only
97 | ```
98 |
99 | ## Test Coverage
100 |
101 | ### Core Operations
102 | - Server initialization and tool discovery
103 | - Adding memories (text, JSON, message)
104 | - Episode queue management
105 | - Search operations (semantic, hybrid)
106 | - Episode retrieval and deletion
107 | - Entity and edge operations
108 |
109 | ### Async Operations
110 | - Concurrent operations
111 | - Queue management
112 | - Sequential processing within groups
113 | - Parallel processing across groups
114 |
115 | ### Performance Testing
116 | - Latency measurement
117 | - Throughput testing
118 | - Batch processing
119 | - Resource usage monitoring
120 |
121 | ### Stress Testing
122 | - Sustained load scenarios
123 | - Spike load handling
124 | - Memory leak detection
125 | - Connection pool exhaustion
126 | - Rate limit handling
127 |
128 | ## Configuration
129 |
130 | ### Environment Variables
131 |
132 | ```bash
133 | # Database configuration
134 | export DATABASE_PROVIDER=falkordb # or neo4j
135 | export NEO4J_URI=bolt://localhost:7687
136 | export NEO4J_USER=neo4j
137 | export NEO4J_PASSWORD=graphiti
138 | export FALKORDB_URI=redis://localhost:6379
139 |
140 | # LLM configuration
141 | export OPENAI_API_KEY=your_key_here # or use --mock-llm
142 |
143 | # Test configuration
144 | export TEST_MODE=true
145 | export LOG_LEVEL=INFO
146 | ```
147 |
148 | ### pytest.ini Configuration
149 |
150 | The `pytest.ini` file configures:
151 | - Test discovery patterns
152 | - Async mode settings
153 | - Test markers
154 | - Timeout settings
155 | - Output formatting
156 |
157 | ## Test Fixtures
158 |
159 | ### Data Generation
160 |
161 | The test suite includes comprehensive data generators:
162 |
163 | ```python
164 | from test_fixtures import TestDataGenerator
165 |
166 | # Generate test data
167 | company = TestDataGenerator.generate_company_profile()
168 | conversation = TestDataGenerator.generate_conversation()
169 | document = TestDataGenerator.generate_technical_document()
170 | ```
171 |
172 | ### Test Client
173 |
174 | Simplified client creation:
175 |
176 | ```python
177 | from test_fixtures import graphiti_test_client
178 |
179 | async with graphiti_test_client(database="falkordb") as (session, group_id):
180 | # Use session for testing
181 | result = await session.call_tool('add_memory', {...})
182 | ```
183 |
184 | ## Performance Considerations
185 |
186 | ### LLM Latency Management
187 |
188 | The tests account for LLM inference latency through:
189 |
190 | 1. **Configurable timeouts** - Different timeouts for different operations
191 | 2. **Mock LLM option** - Fast testing without API calls
192 | 3. **Intelligent polling** - Adaptive waiting for episode processing
193 | 4. **Batch operations** - Testing efficiency of batched requests
194 |
195 | ### Resource Management
196 |
197 | - Memory leak detection
198 | - Connection pool monitoring
199 | - Resource usage tracking
200 | - Graceful degradation testing
201 |
202 | ## CI/CD Integration
203 |
204 | ### GitHub Actions
205 |
206 | ```yaml
207 | name: MCP Integration Tests
208 |
209 | on: [push, pull_request]
210 |
211 | jobs:
212 | test:
213 | runs-on: ubuntu-latest
214 |
215 | services:
216 | neo4j:
217 | image: neo4j:5.26
218 | env:
219 | NEO4J_AUTH: neo4j/graphiti
220 | ports:
221 | - 7687:7687
222 |
223 | steps:
224 | - uses: actions/checkout@v2
225 |
226 | - name: Install dependencies
227 | run: |
228 | pip install uv
229 | uv sync --extra dev
230 |
231 | - name: Run smoke tests
232 | run: python tests/run_tests.py smoke --mock-llm
233 |
234 | - name: Run integration tests
235 | run: python tests/run_tests.py integration --database neo4j
236 | env:
237 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
238 | ```
239 |
240 | ## Troubleshooting
241 |
242 | ### Common Issues
243 |
244 | 1. **Database connection failures**
245 | ```bash
246 | # Check Neo4j
247 | curl http://localhost:7474
248 |
249 | # Check FalkorDB
250 | redis-cli ping
251 | ```
252 |
253 | 2. **API key issues**
254 | ```bash
255 | # Use mock LLM for testing without API key
256 | python tests/run_tests.py all --mock-llm
257 | ```
258 |
259 | 3. **Timeout errors**
260 | ```bash
261 | # Increase timeout for slow systems
262 | python tests/run_tests.py integration --timeout 600
263 | ```
264 |
265 | 4. **Memory issues**
266 | ```bash
267 | # Skip stress tests on low-memory systems
268 | python tests/run_tests.py all --skip-slow
269 | ```
270 |
271 | ## Test Reports
272 |
273 | ### Performance Report
274 |
275 | After running performance tests:
276 |
277 | ```python
278 | from test_fixtures import PerformanceBenchmark
279 |
280 | benchmark = PerformanceBenchmark()
281 | # ... run tests ...
282 | print(benchmark.report())
283 | ```
284 |
285 | ### Load Test Report
286 |
287 | Stress tests generate detailed reports:
288 |
289 | ```
290 | LOAD TEST REPORT
291 | ================
292 | Test Run 1:
293 | Total Operations: 100
294 | Success Rate: 95.0%
295 | Throughput: 12.5 ops/s
296 | Latency (avg/p50/p95/p99/max): 0.8/0.7/1.5/2.1/3.2s
297 | ```
298 |
299 | ## Contributing
300 |
301 | When adding new tests:
302 |
303 | 1. Use appropriate pytest markers
304 | 2. Include docstrings explaining test purpose
305 | 3. Use fixtures for common operations
306 | 4. Consider LLM latency in test design
307 | 5. Add timeout handling for long operations
308 | 6. Include performance metrics where relevant
309 |
310 | ## License
311 |
312 | See main project LICENSE file.
```
--------------------------------------------------------------------------------
/mcp_server/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Graphiti MCP Server
2 |
3 | Graphiti is a framework for building and querying temporally-aware knowledge graphs, specifically tailored for AI agents
4 | operating in dynamic environments. Unlike traditional retrieval-augmented generation (RAG) methods, Graphiti
5 | continuously integrates user interactions, structured and unstructured enterprise data, and external information into a
6 | coherent, queryable graph. The framework supports incremental data updates, efficient retrieval, and precise historical
7 | queries without requiring complete graph recomputation, making it suitable for developing interactive, context-aware AI
8 | applications.
9 |
10 | This is an experimental Model Context Protocol (MCP) server implementation for Graphiti. The MCP server exposes
11 | Graphiti's key functionality through the MCP protocol, allowing AI assistants to interact with Graphiti's knowledge
12 | graph capabilities.
13 |
14 | ## Features
15 |
16 | The Graphiti MCP server provides comprehensive knowledge graph capabilities:
17 |
18 | - **Episode Management**: Add, retrieve, and delete episodes (text, messages, or JSON data)
19 | - **Entity Management**: Search and manage entity nodes and relationships in the knowledge graph
20 | - **Search Capabilities**: Search for facts (edges) and node summaries using semantic and hybrid search
21 | - **Group Management**: Organize and manage groups of related data with group_id filtering
22 | - **Graph Maintenance**: Clear the graph and rebuild indices
23 | - **Graph Database Support**: Multiple backend options including FalkorDB (default) and Neo4j
24 | - **Multiple LLM Providers**: Support for OpenAI, Anthropic, Gemini, Groq, and Azure OpenAI
25 | - **Multiple Embedding Providers**: Support for OpenAI, Voyage, Sentence Transformers, and Gemini embeddings
26 | - **Rich Entity Types**: Built-in entity types including Preferences, Requirements, Procedures, Locations, Events, Organizations, Documents, and more for structured knowledge extraction
27 | - **HTTP Transport**: Default HTTP transport with MCP endpoint at `/mcp/` for broad client compatibility
28 | - **Queue-based Processing**: Asynchronous episode processing with configurable concurrency limits
29 |
30 | ## Quick Start
31 |
32 | ### Clone the Graphiti GitHub repo
33 |
34 | ```bash
35 | git clone https://github.com/getzep/graphiti.git
36 | ```
37 |
38 | or
39 |
40 | ```bash
41 | gh repo clone getzep/graphiti
42 | ```
43 |
44 | ### For Claude Desktop and other `stdio` only clients
45 |
46 | 1. Note the full path to this directory.
47 |
48 | ```
49 | cd graphiti && pwd
50 | ```
51 |
52 | 2. Install the [Graphiti prerequisites](#prerequisites).
53 |
54 | 3. Configure Claude, Cursor, or other MCP client to use [Graphiti with a `stdio` transport](#integrating-with-mcp-clients). See the client documentation on where to find their MCP configuration files.
55 |
56 | ### For Cursor and other HTTP-enabled clients
57 |
58 | 1. Change directory to the `mcp_server` directory
59 |
60 | `cd graphiti/mcp_server`
61 |
62 | 2. Start the combined FalkorDB + MCP server using Docker Compose (recommended)
63 |
64 | ```bash
65 | docker compose up
66 | ```
67 |
68 | This starts both FalkorDB and the MCP server in a single container.
69 |
70 | **Alternative**: Run with separate containers using Neo4j:
71 | ```bash
72 | docker compose -f docker/docker-compose-neo4j.yml up
73 | ```
74 |
75 | 4. Point your MCP client to `http://localhost:8000/mcp/`
76 |
77 | ## Installation
78 |
79 | ### Prerequisites
80 |
81 | 1. Docker and Docker Compose (for the default FalkorDB setup)
82 | 2. OpenAI API key for LLM operations (or API keys for other supported LLM providers)
83 | 3. (Optional) Python 3.10+ if running the MCP server standalone with an external FalkorDB instance
84 |
85 | ### Setup
86 |
87 | 1. Clone the repository and navigate to the mcp_server directory
88 | 2. Use `uv` to create a virtual environment and install dependencies:
89 |
90 | ```bash
91 | # Install uv if you don't have it already
92 | curl -LsSf https://astral.sh/uv/install.sh | sh
93 |
94 | # Create a virtual environment and install dependencies in one step
95 | uv sync
96 |
97 | # Optional: Install additional LLM providers (anthropic, gemini, groq, voyage, sentence-transformers)
98 | uv sync --extra providers
99 | ```
100 |
101 | ## Configuration
102 |
103 | The server can be configured using a `config.yaml` file, environment variables, or command-line arguments (in order of precedence).
104 |
105 | ### Default Configuration
106 |
107 | The MCP server comes with sensible defaults:
108 | - **Transport**: HTTP (accessible at `http://localhost:8000/mcp/`)
109 | - **Database**: FalkorDB (combined in single container with MCP server)
110 | - **LLM**: OpenAI with model gpt-5-mini
111 | - **Embedder**: OpenAI text-embedding-3-small
112 |
113 | ### Database Configuration
114 |
115 | #### FalkorDB (Default)
116 |
117 | FalkorDB is a Redis-based graph database that comes bundled with the MCP server in a single Docker container. This is the default and recommended setup.
118 |
119 | ```yaml
120 | database:
121 | provider: "falkordb" # Default
122 | providers:
123 | falkordb:
124 | uri: "redis://localhost:6379"
125 | password: "" # Optional
126 | database: "default_db" # Optional
127 | ```
128 |
129 | #### Neo4j
130 |
131 | For production use or when you need a full-featured graph database, Neo4j is recommended:
132 |
133 | ```yaml
134 | database:
135 | provider: "neo4j"
136 | providers:
137 | neo4j:
138 | uri: "bolt://localhost:7687"
139 | username: "neo4j"
140 | password: "your_password"
141 | database: "neo4j" # Optional, defaults to "neo4j"
142 | ```
143 |
144 | #### FalkorDB
145 |
146 | FalkorDB is another graph database option based on Redis:
147 |
148 | ```yaml
149 | database:
150 | provider: "falkordb"
151 | providers:
152 | falkordb:
153 | uri: "redis://localhost:6379"
154 | password: "" # Optional
155 | database: "default_db" # Optional
156 | ```
157 |
158 | ### Configuration File (config.yaml)
159 |
160 | The server supports multiple LLM providers (OpenAI, Anthropic, Gemini, Groq) and embedders. Edit `config.yaml` to configure:
161 |
162 | ```yaml
163 | server:
164 | transport: "http" # Default. Options: stdio, http
165 |
166 | llm:
167 | provider: "openai" # or "anthropic", "gemini", "groq", "azure_openai"
168 | model: "gpt-4.1" # Default model
169 |
170 | database:
171 | provider: "falkordb" # Default. Options: "falkordb", "neo4j"
172 | ```
173 |
174 | ### Using Ollama for Local LLM
175 |
176 | To use Ollama with the MCP server, configure it as an OpenAI-compatible endpoint:
177 |
178 | ```yaml
179 | llm:
180 | provider: "openai"
181 | model: "gpt-oss:120b" # or your preferred Ollama model
182 | api_base: "http://localhost:11434/v1"
183 | api_key: "ollama" # dummy key required
184 |
185 | embedder:
186 | provider: "sentence_transformers" # recommended for local setup
187 | model: "all-MiniLM-L6-v2"
188 | ```
189 |
190 | Make sure Ollama is running locally with: `ollama serve`
191 |
192 | ### Entity Types
193 |
194 | Graphiti MCP Server includes built-in entity types for structured knowledge extraction. These entity types are always enabled and configured via the `entity_types` section in your `config.yaml`:
195 |
196 | **Available Entity Types:**
197 |
198 | - **Preference**: User preferences, choices, opinions, or selections (prioritized for user-specific information)
199 | - **Requirement**: Specific needs, features, or functionality that must be fulfilled
200 | - **Procedure**: Standard operating procedures and sequential instructions
201 | - **Location**: Physical or virtual places where activities occur
202 | - **Event**: Time-bound activities, occurrences, or experiences
203 | - **Organization**: Companies, institutions, groups, or formal entities
204 | - **Document**: Information content in various forms (books, articles, reports, videos, etc.)
205 | - **Topic**: Subject of conversation, interest, or knowledge domain (used as a fallback)
206 | - **Object**: Physical items, tools, devices, or possessions (used as a fallback)
207 |
208 | These entity types are defined in `config.yaml` and can be customized by modifying the descriptions:
209 |
210 | ```yaml
211 | graphiti:
212 | entity_types:
213 | - name: "Preference"
214 | description: "User preferences, choices, opinions, or selections"
215 | - name: "Requirement"
216 | description: "Specific needs, features, or functionality"
217 | # ... additional entity types
218 | ```
219 |
220 | The MCP server automatically uses these entity types during episode ingestion to extract and structure information from conversations and documents.
221 |
222 | ### Environment Variables
223 |
224 | The `config.yaml` file supports environment variable expansion using `${VAR_NAME}` or `${VAR_NAME:default}` syntax. Key variables:
225 |
226 | - `NEO4J_URI`: URI for the Neo4j database (default: `bolt://localhost:7687`)
227 | - `NEO4J_USER`: Neo4j username (default: `neo4j`)
228 | - `NEO4J_PASSWORD`: Neo4j password (default: `demodemo`)
229 | - `OPENAI_API_KEY`: OpenAI API key (required for OpenAI LLM/embedder)
230 | - `ANTHROPIC_API_KEY`: Anthropic API key (for Claude models)
231 | - `GOOGLE_API_KEY`: Google API key (for Gemini models)
232 | - `GROQ_API_KEY`: Groq API key (for Groq models)
233 | - `AZURE_OPENAI_API_KEY`: Azure OpenAI API key
234 | - `AZURE_OPENAI_ENDPOINT`: Azure OpenAI endpoint URL
235 | - `AZURE_OPENAI_DEPLOYMENT`: Azure OpenAI deployment name
236 | - `AZURE_OPENAI_EMBEDDINGS_ENDPOINT`: Optional Azure OpenAI embeddings endpoint URL
237 | - `AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT`: Optional Azure OpenAI embeddings deployment name
238 | - `AZURE_OPENAI_API_VERSION`: Optional Azure OpenAI API version
239 | - `USE_AZURE_AD`: Optional use Azure Managed Identities for authentication
240 | - `SEMAPHORE_LIMIT`: Episode processing concurrency. See [Concurrency and LLM Provider 429 Rate Limit Errors](#concurrency-and-llm-provider-429-rate-limit-errors)
241 |
242 | You can set these variables in a `.env` file in the project directory.
243 |
244 | ## Running the Server
245 |
246 | ### Default Setup (FalkorDB Combined Container)
247 |
248 | To run the Graphiti MCP server with the default FalkorDB setup:
249 |
250 | ```bash
251 | docker compose up
252 | ```
253 |
254 | This starts a single container with:
255 | - HTTP transport on `http://localhost:8000/mcp/`
256 | - FalkorDB graph database on `localhost:6379`
257 | - FalkorDB web UI on `http://localhost:3000`
258 | - OpenAI LLM with gpt-5-mini model
259 |
260 | ### Running with Neo4j
261 |
262 | #### Option 1: Using Docker Compose
263 |
264 | The easiest way to run with Neo4j is using the provided Docker Compose configuration:
265 |
266 | ```bash
267 | # This starts both Neo4j and the MCP server
268 | docker compose -f docker/docker-compose.neo4j.yaml up
269 | ```
270 |
271 | #### Option 2: Direct Execution with Existing Neo4j
272 |
273 | If you have Neo4j already running:
274 |
275 | ```bash
276 | # Set environment variables
277 | export NEO4J_URI="bolt://localhost:7687"
278 | export NEO4J_USER="neo4j"
279 | export NEO4J_PASSWORD="your_password"
280 |
281 | # Run with Neo4j
282 | uv run graphiti_mcp_server.py --database-provider neo4j
283 | ```
284 |
285 | Or use the Neo4j configuration file:
286 |
287 | ```bash
288 | uv run graphiti_mcp_server.py --config config/config-docker-neo4j.yaml
289 | ```
290 |
291 | ### Running with FalkorDB
292 |
293 | #### Option 1: Using Docker Compose
294 |
295 | ```bash
296 | # This starts both FalkorDB (Redis-based) and the MCP server
297 | docker compose -f docker/docker-compose.falkordb.yaml up
298 | ```
299 |
300 | #### Option 2: Direct Execution with Existing FalkorDB
301 |
302 | ```bash
303 | # Set environment variables
304 | export FALKORDB_URI="redis://localhost:6379"
305 | export FALKORDB_PASSWORD="" # If password protected
306 |
307 | # Run with FalkorDB
308 | uv run graphiti_mcp_server.py --database-provider falkordb
309 | ```
310 |
311 | Or use the FalkorDB configuration file:
312 |
313 | ```bash
314 | uv run graphiti_mcp_server.py --config config/config-docker-falkordb.yaml
315 | ```
316 |
317 | ### Available Command-Line Arguments
318 |
319 | - `--config`: Path to YAML configuration file (default: config.yaml)
320 | - `--llm-provider`: LLM provider to use (openai, anthropic, gemini, groq, azure_openai)
321 | - `--embedder-provider`: Embedder provider to use (openai, azure_openai, gemini, voyage)
322 | - `--database-provider`: Database provider to use (falkordb, neo4j) - default: falkordb
323 | - `--model`: Model name to use with the LLM client
324 | - `--temperature`: Temperature setting for the LLM (0.0-2.0)
325 | - `--transport`: Choose the transport method (http or stdio, default: http)
326 | - `--group-id`: Set a namespace for the graph (optional). If not provided, defaults to "main"
327 | - `--destroy-graph`: If set, destroys all Graphiti graphs on startup
328 |
329 | ### Concurrency and LLM Provider 429 Rate Limit Errors
330 |
331 | Graphiti's ingestion pipelines are designed for high concurrency, controlled by the `SEMAPHORE_LIMIT` environment variable. This setting determines how many episodes can be processed simultaneously. Since each episode involves multiple LLM calls (entity extraction, deduplication, summarization), the actual number of concurrent LLM requests will be several times higher.
332 |
333 | **Default:** `SEMAPHORE_LIMIT=10` (suitable for OpenAI Tier 3, mid-tier Anthropic)
334 |
335 | #### Tuning Guidelines by LLM Provider
336 |
337 | **OpenAI:**
338 | - Tier 1 (free): 3 RPM → `SEMAPHORE_LIMIT=1-2`
339 | - Tier 2: 60 RPM → `SEMAPHORE_LIMIT=5-8`
340 | - Tier 3: 500 RPM → `SEMAPHORE_LIMIT=10-15`
341 | - Tier 4: 5,000 RPM → `SEMAPHORE_LIMIT=20-50`
342 |
343 | **Anthropic:**
344 | - Default tier: 50 RPM → `SEMAPHORE_LIMIT=5-8`
345 | - High tier: 1,000 RPM → `SEMAPHORE_LIMIT=15-30`
346 |
347 | **Azure OpenAI:**
348 | - Consult your quota in Azure Portal and adjust accordingly
349 | - Start conservative and increase gradually
350 |
351 | **Ollama (local):**
352 | - Hardware dependent → `SEMAPHORE_LIMIT=1-5`
353 | - Monitor CPU/GPU usage and adjust
354 |
355 | #### Symptoms
356 |
357 | - **Too high**: 429 rate limit errors, increased API costs from parallel processing
358 | - **Too low**: Slow episode throughput, underutilized API quota
359 |
360 | #### Monitoring
361 |
362 | - Watch logs for `429` rate limit errors
363 | - Monitor episode processing times in server logs
364 | - Check your LLM provider's dashboard for actual request rates
365 | - Track token usage and costs
366 |
367 | Set this in your `.env` file:
368 | ```bash
369 | SEMAPHORE_LIMIT=10 # Adjust based on your LLM provider tier
370 | ```
371 |
372 | ### Docker Deployment
373 |
374 | The Graphiti MCP server can be deployed using Docker with your choice of database backend. The Dockerfile uses `uv` for package management, ensuring consistent dependency installation.
375 |
376 | A pre-built Graphiti MCP container is available at: `zepai/knowledge-graph-mcp`
377 |
378 | #### Environment Configuration
379 |
380 | Before running Docker Compose, configure your API keys using a `.env` file (recommended):
381 |
382 | 1. **Create a .env file in the mcp_server directory**:
383 | ```bash
384 | cd graphiti/mcp_server
385 | cp .env.example .env
386 | ```
387 |
388 | 2. **Edit the .env file** to set your API keys:
389 | ```bash
390 | # Required - at least one LLM provider API key
391 | OPENAI_API_KEY=your_openai_api_key_here
392 |
393 | # Optional - other LLM providers
394 | ANTHROPIC_API_KEY=your_anthropic_key
395 | GOOGLE_API_KEY=your_google_key
396 | GROQ_API_KEY=your_groq_key
397 |
398 | # Optional - embedder providers
399 | VOYAGE_API_KEY=your_voyage_key
400 | ```
401 |
402 | **Important**: The `.env` file must be in the `mcp_server/` directory (the parent of the `docker/` subdirectory).
403 |
404 | #### Running with Docker Compose
405 |
406 | **All commands must be run from the `mcp_server` directory** to ensure the `.env` file is loaded correctly:
407 |
408 | ```bash
409 | cd graphiti/mcp_server
410 | ```
411 |
412 | ##### Option 1: FalkorDB Combined Container (Default)
413 |
414 | Single container with both FalkorDB and MCP server - simplest option:
415 |
416 | ```bash
417 | docker compose up
418 | ```
419 |
420 | ##### Option 2: Neo4j Database
421 |
422 | Separate containers with Neo4j and MCP server:
423 |
424 | ```bash
425 | docker compose -f docker/docker-compose-neo4j.yml up
426 | ```
427 |
428 | Default Neo4j credentials:
429 | - Username: `neo4j`
430 | - Password: `demodemo`
431 | - Bolt URI: `bolt://neo4j:7687`
432 | - Browser UI: `http://localhost:7474`
433 |
434 | ##### Option 3: FalkorDB with Separate Containers
435 |
436 | Alternative setup with separate FalkorDB and MCP server containers:
437 |
438 | ```bash
439 | docker compose -f docker/docker-compose-falkordb.yml up
440 | ```
441 |
442 | FalkorDB configuration:
443 | - Redis port: `6379`
444 | - Web UI: `http://localhost:3000`
445 | - Connection: `redis://falkordb:6379`
446 |
447 | #### Accessing the MCP Server
448 |
449 | Once running, the MCP server is available at:
450 | - **HTTP endpoint**: `http://localhost:8000/mcp/`
451 | - **Health check**: `http://localhost:8000/health`
452 |
453 | #### Running Docker Compose from a Different Directory
454 |
455 | If you run Docker Compose from the `docker/` subdirectory instead of `mcp_server/`, you'll need to modify the `.env` file path in the compose file:
456 |
457 | ```yaml
458 | # Change this line in the docker-compose file:
459 | env_file:
460 | - path: ../.env # When running from mcp_server/
461 |
462 | # To this:
463 | env_file:
464 | - path: .env # When running from mcp_server/docker/
465 | ```
466 |
467 | However, **running from the `mcp_server/` directory is recommended** to avoid confusion.
468 |
469 | ## Integrating with MCP Clients
470 |
471 | ### VS Code / GitHub Copilot
472 |
473 | VS Code with GitHub Copilot Chat extension supports MCP servers. Add to your VS Code settings (`.vscode/mcp.json` or global settings):
474 |
475 | ```json
476 | {
477 | "mcpServers": {
478 | "graphiti": {
479 | "uri": "http://localhost:8000/mcp/",
480 | "transport": {
481 | "type": "http"
482 | }
483 | }
484 | }
485 | }
486 | ```
487 |
488 | ### Other MCP Clients
489 |
490 | To use the Graphiti MCP server with other MCP-compatible clients, configure it to connect to the server:
491 |
492 | > [!IMPORTANT]
493 | > You will need the Python package manager, `uv` installed. Please refer to the [`uv` install instructions](https://docs.astral.sh/uv/getting-started/installation/).
494 | >
495 | > Ensure that you set the full path to the `uv` binary and your Graphiti project folder.
496 |
497 | ```json
498 | {
499 | "mcpServers": {
500 | "graphiti-memory": {
501 | "transport": "stdio",
502 | "command": "/Users/<user>/.local/bin/uv",
503 | "args": [
504 | "run",
505 | "--isolated",
506 | "--directory",
507 | "/Users/<user>>/dev/zep/graphiti/mcp_server",
508 | "--project",
509 | ".",
510 | "graphiti_mcp_server.py",
511 | "--transport",
512 | "stdio"
513 | ],
514 | "env": {
515 | "NEO4J_URI": "bolt://localhost:7687",
516 | "NEO4J_USER": "neo4j",
517 | "NEO4J_PASSWORD": "password",
518 | "OPENAI_API_KEY": "sk-XXXXXXXX",
519 | "MODEL_NAME": "gpt-4.1-mini"
520 | }
521 | }
522 | }
523 | }
524 | ```
525 |
526 | For HTTP transport (default), you can use this configuration:
527 |
528 | ```json
529 | {
530 | "mcpServers": {
531 | "graphiti-memory": {
532 | "transport": "http",
533 | "url": "http://localhost:8000/mcp/"
534 | }
535 | }
536 | }
537 | ```
538 |
539 | ## Available Tools
540 |
541 | The Graphiti MCP server exposes the following tools:
542 |
543 | - `add_episode`: Add an episode to the knowledge graph (supports text, JSON, and message formats)
544 | - `search_nodes`: Search the knowledge graph for relevant node summaries
545 | - `search_facts`: Search the knowledge graph for relevant facts (edges between entities)
546 | - `delete_entity_edge`: Delete an entity edge from the knowledge graph
547 | - `delete_episode`: Delete an episode from the knowledge graph
548 | - `get_entity_edge`: Get an entity edge by its UUID
549 | - `get_episodes`: Get the most recent episodes for a specific group
550 | - `clear_graph`: Clear all data from the knowledge graph and rebuild indices
551 | - `get_status`: Get the status of the Graphiti MCP server and Neo4j connection
552 |
553 | ## Working with JSON Data
554 |
555 | The Graphiti MCP server can process structured JSON data through the `add_episode` tool with `source="json"`. This
556 | allows you to automatically extract entities and relationships from structured data:
557 |
558 | ```
559 |
560 | add_episode(
561 | name="Customer Profile",
562 | episode_body="{\"company\": {\"name\": \"Acme Technologies\"}, \"products\": [{\"id\": \"P001\", \"name\": \"CloudSync\"}, {\"id\": \"P002\", \"name\": \"DataMiner\"}]}",
563 | source="json",
564 | source_description="CRM data"
565 | )
566 |
567 | ```
568 |
569 | ## Integrating with the Cursor IDE
570 |
571 | To integrate the Graphiti MCP Server with the Cursor IDE, follow these steps:
572 |
573 | 1. Run the Graphiti MCP server using the default HTTP transport:
574 |
575 | ```bash
576 | uv run graphiti_mcp_server.py --group-id <your_group_id>
577 | ```
578 |
579 | Hint: specify a `group_id` to namespace graph data. If you do not specify a `group_id`, the server will use "main" as the group_id.
580 |
581 | or
582 |
583 | ```bash
584 | docker compose up
585 | ```
586 |
587 | 2. Configure Cursor to connect to the Graphiti MCP server.
588 |
589 | ```json
590 | {
591 | "mcpServers": {
592 | "graphiti-memory": {
593 | "url": "http://localhost:8000/mcp/"
594 | }
595 | }
596 | }
597 | ```
598 |
599 | 3. Add the Graphiti rules to Cursor's User Rules. See [cursor_rules.md](cursor_rules.md) for details.
600 |
601 | 4. Kick off an agent session in Cursor.
602 |
603 | The integration enables AI assistants in Cursor to maintain persistent memory through Graphiti's knowledge graph
604 | capabilities.
605 |
606 | ## Integrating with Claude Desktop (Docker MCP Server)
607 |
608 | The Graphiti MCP Server uses HTTP transport (at endpoint `/mcp/`). Claude Desktop does not natively support HTTP transport, so you'll need to use a gateway like `mcp-remote`.
609 |
610 | 1. **Run the Graphiti MCP server**:
611 |
612 | ```bash
613 | docker compose up
614 | # Or run directly with uv:
615 | uv run graphiti_mcp_server.py
616 | ```
617 |
618 | 2. **(Optional) Install `mcp-remote` globally**:
619 | If you prefer to have `mcp-remote` installed globally, or if you encounter issues with `npx` fetching the package, you can install it globally. Otherwise, `npx` (used in the next step) will handle it for you.
620 |
621 | ```bash
622 | npm install -g mcp-remote
623 | ```
624 |
625 | 3. **Configure Claude Desktop**:
626 | Open your Claude Desktop configuration file (usually `claude_desktop_config.json`) and add or modify the `mcpServers` section as follows:
627 |
628 | ```json
629 | {
630 | "mcpServers": {
631 | "graphiti-memory": {
632 | // You can choose a different name if you prefer
633 | "command": "npx", // Or the full path to mcp-remote if npx is not in your PATH
634 | "args": [
635 | "mcp-remote",
636 | "http://localhost:8000/mcp/" // The Graphiti server's HTTP endpoint
637 | ]
638 | }
639 | }
640 | }
641 | ```
642 |
643 | If you already have an `mcpServers` entry, add `graphiti-memory` (or your chosen name) as a new key within it.
644 |
645 | 4. **Restart Claude Desktop** for the changes to take effect.
646 |
647 | ## Requirements
648 |
649 | - Python 3.10 or higher
650 | - OpenAI API key (for LLM operations and embeddings) or other LLM provider API keys
651 | - MCP-compatible client
652 | - Docker and Docker Compose (for the default FalkorDB combined container)
653 | - (Optional) Neo4j database (version 5.26 or later) if not using the default FalkorDB setup
654 |
655 | ## Telemetry
656 |
657 | The Graphiti MCP server uses the Graphiti core library, which includes anonymous telemetry collection. When you initialize the Graphiti MCP server, anonymous usage statistics are collected to help improve the framework.
658 |
659 | ### What's Collected
660 |
661 | - Anonymous identifier and system information (OS, Python version)
662 | - Graphiti version and configuration choices (LLM provider, database backend, embedder type)
663 | - **No personal data, API keys, or actual graph content is ever collected**
664 |
665 | ### How to Disable
666 |
667 | To disable telemetry in the MCP server, set the environment variable:
668 |
669 | ```bash
670 | export GRAPHITI_TELEMETRY_ENABLED=false
671 | ```
672 |
673 | Or add it to your `.env` file:
674 |
675 | ```
676 | GRAPHITI_TELEMETRY_ENABLED=false
677 | ```
678 |
679 | For complete details about what's collected and why, see the [Telemetry section in the main Graphiti README](../README.md#telemetry).
680 |
681 | ## License
682 |
683 | This project is licensed under the same license as the parent Graphiti project.
684 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | <p align="center">
2 | <a href="https://www.getzep.com/">
3 | <img src="https://github.com/user-attachments/assets/119c5682-9654-4257-8922-56b7cb8ffd73" width="150" alt="Zep Logo">
4 | </a>
5 | </p>
6 |
7 | <h1 align="center">
8 | Graphiti
9 | </h1>
10 | <h2 align="center"> Build Real-Time Knowledge Graphs for AI Agents</h2>
11 | <div align="center">
12 |
13 | [](https://github.com/getzep/Graphiti/actions/workflows/lint.yml)
14 | [](https://github.com/getzep/Graphiti/actions/workflows/unit_tests.yml)
15 | [](https://github.com/getzep/Graphiti/actions/workflows/typecheck.yml)
16 |
17 | 
18 | [](https://discord.com/invite/W8Kw6bsgXQ)
19 | [](https://arxiv.org/abs/2501.13956)
20 | [](https://github.com/getzep/graphiti/releases)
21 |
22 | </div>
23 | <div align="center">
24 |
25 | <a href="https://trendshift.io/repositories/12986" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12986" alt="getzep%2Fgraphiti | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
26 |
27 | </div>
28 |
29 | :star: _Help us reach more developers and grow the Graphiti community. Star this repo!_
30 |
31 | <br />
32 |
33 | > [!TIP]
34 | > Check out the new [MCP server for Graphiti](mcp_server/README.md)! Give Claude, Cursor, and other MCP clients powerful
35 | > Knowledge Graph-based memory.
36 |
37 | Graphiti is a framework for building and querying temporally-aware knowledge graphs, specifically tailored for AI agents
38 | operating in dynamic environments. Unlike traditional retrieval-augmented generation (RAG) methods, Graphiti
39 | continuously integrates user interactions, structured and unstructured enterprise data, and external information into a
40 | coherent, queryable graph. The framework supports incremental data updates, efficient retrieval, and precise historical
41 | queries without requiring complete graph recomputation, making it suitable for developing interactive, context-aware AI
42 | applications.
43 |
44 | Use Graphiti to:
45 |
46 | - Integrate and maintain dynamic user interactions and business data.
47 | - Facilitate state-based reasoning and task automation for agents.
48 | - Query complex, evolving data with semantic, keyword, and graph-based search methods.
49 |
50 | <br />
51 |
52 | <p align="center">
53 | <img src="images/graphiti-graph-intro.gif" alt="Graphiti temporal walkthrough" width="700px">
54 | </p>
55 |
56 | <br />
57 |
58 | A knowledge graph is a network of interconnected facts, such as _"Kendra loves Adidas shoes."_ Each fact is a "triplet"
59 | represented by two entities, or
60 | nodes ("Kendra", "Adidas shoes"), and their relationship, or edge ("loves"). Knowledge Graphs have been explored
61 | extensively for information retrieval. What makes Graphiti unique is its ability to autonomously build a knowledge graph
62 | while handling changing relationships and maintaining historical context.
63 |
64 | ## Graphiti and Zep's Context Engineering Platform.
65 |
66 | Graphiti powers the core of [Zep](https://www.getzep.com), a turn-key context engineering platform for AI Agents. Zep
67 | offers agent memory, Graph RAG for dynamic data, and context retrieval and assembly.
68 |
69 | Using Graphiti, we've demonstrated Zep is
70 | the [State of the Art in Agent Memory](https://blog.getzep.com/state-of-the-art-agent-memory/).
71 |
72 | Read our paper: [Zep: A Temporal Knowledge Graph Architecture for Agent Memory](https://arxiv.org/abs/2501.13956).
73 |
74 | We're excited to open-source Graphiti, believing its potential reaches far beyond AI memory applications.
75 |
76 | <p align="center">
77 | <a href="https://arxiv.org/abs/2501.13956"><img src="images/arxiv-screenshot.png" alt="Zep: A Temporal Knowledge Graph Architecture for Agent Memory" width="700px"></a>
78 | </p>
79 |
80 | ## Zep vs Graphiti
81 |
82 | | Aspect | Zep | Graphiti |
83 | |--------|-----|----------|
84 | | **What they are** | Fully managed platform for context engineering and AI memory | Open-source graph framework |
85 | | **User & conversation management** | Built-in users, threads, and message storage | Build your own |
86 | | **Retrieval & performance** | Pre-configured, production-ready retrieval with sub-200ms performance at scale | Custom implementation required; performance depends on your setup |
87 | | **Developer tools** | Dashboard with graph visualization, debug logs, API logs; SDKs for Python, TypeScript, and Go | Build your own tools |
88 | | **Enterprise features** | SLAs, support, security guarantees | Self-managed |
89 | | **Deployment** | Fully managed or in your cloud | Self-hosted only |
90 |
91 | ### When to choose which
92 |
93 | **Choose Zep** if you want a turnkey, enterprise-grade platform with security, performance, and support baked in.
94 |
95 | **Choose Graphiti** if you want a flexible OSS core and you're comfortable building/operating the surrounding system.
96 |
97 | ## Why Graphiti?
98 |
99 | Traditional RAG approaches often rely on batch processing and static data summarization, making them inefficient for
100 | frequently changing data. Graphiti addresses these challenges by providing:
101 |
102 | - **Real-Time Incremental Updates:** Immediate integration of new data episodes without batch recomputation.
103 | - **Bi-Temporal Data Model:** Explicit tracking of event occurrence and ingestion times, allowing accurate point-in-time
104 | queries.
105 | - **Efficient Hybrid Retrieval:** Combines semantic embeddings, keyword (BM25), and graph traversal to achieve
106 | low-latency queries without reliance on LLM summarization.
107 | - **Custom Entity Definitions:** Flexible ontology creation and support for developer-defined entities through
108 | straightforward Pydantic models.
109 | - **Scalability:** Efficiently manages large datasets with parallel processing, suitable for enterprise environments.
110 |
111 | <p align="center">
112 | <img src="/images/graphiti-intro-slides-stock-2.gif" alt="Graphiti structured + unstructured demo" width="700px">
113 | </p>
114 |
115 | ## Graphiti vs. GraphRAG
116 |
117 | | Aspect | GraphRAG | Graphiti |
118 | |----------------------------|---------------------------------------|--------------------------------------------------|
119 | | **Primary Use** | Static document summarization | Dynamic data management |
120 | | **Data Handling** | Batch-oriented processing | Continuous, incremental updates |
121 | | **Knowledge Structure** | Entity clusters & community summaries | Episodic data, semantic entities, communities |
122 | | **Retrieval Method** | Sequential LLM summarization | Hybrid semantic, keyword, and graph-based search |
123 | | **Adaptability** | Low | High |
124 | | **Temporal Handling** | Basic timestamp tracking | Explicit bi-temporal tracking |
125 | | **Contradiction Handling** | LLM-driven summarization judgments | Temporal edge invalidation |
126 | | **Query Latency** | Seconds to tens of seconds | Typically sub-second latency |
127 | | **Custom Entity Types** | No | Yes, customizable |
128 | | **Scalability** | Moderate | High, optimized for large datasets |
129 |
130 | Graphiti is specifically designed to address the challenges of dynamic and frequently updated datasets, making it
131 | particularly suitable for applications requiring real-time interaction and precise historical queries.
132 |
133 | ## Installation
134 |
135 | Requirements:
136 |
137 | - Python 3.10 or higher
138 | - Neo4j 5.26 / FalkorDB 1.1.2 / Kuzu 0.11.2 / Amazon Neptune Database Cluster or Neptune Analytics Graph + Amazon
139 | OpenSearch Serverless collection (serves as the full text search backend)
140 | - OpenAI API key (Graphiti defaults to OpenAI for LLM inference and embedding)
141 |
142 | > [!IMPORTANT]
143 | > Graphiti works best with LLM services that support Structured Output (such as OpenAI and Gemini).
144 | > Using other services may result in incorrect output schemas and ingestion failures. This is particularly
145 | > problematic when using smaller models.
146 |
147 | Optional:
148 |
149 | - Google Gemini, Anthropic, or Groq API key (for alternative LLM providers)
150 |
151 | > [!TIP]
152 | > The simplest way to install Neo4j is via [Neo4j Desktop](https://neo4j.com/download/). It provides a user-friendly
153 | > interface to manage Neo4j instances and databases.
154 | > Alternatively, you can use FalkorDB on-premises via Docker and instantly start with the quickstart example:
155 |
156 | ```bash
157 | docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest
158 |
159 | ```
160 |
161 | ```bash
162 | pip install graphiti-core
163 | ```
164 |
165 | or
166 |
167 | ```bash
168 | uv add graphiti-core
169 | ```
170 |
171 | ### Installing with FalkorDB Support
172 |
173 | If you plan to use FalkorDB as your graph database backend, install with the FalkorDB extra:
174 |
175 | ```bash
176 | pip install graphiti-core[falkordb]
177 |
178 | # or with uv
179 | uv add graphiti-core[falkordb]
180 | ```
181 |
182 | ### Installing with Kuzu Support
183 |
184 | If you plan to use Kuzu as your graph database backend, install with the Kuzu extra:
185 |
186 | ```bash
187 | pip install graphiti-core[kuzu]
188 |
189 | # or with uv
190 | uv add graphiti-core[kuzu]
191 | ```
192 |
193 | ### Installing with Amazon Neptune Support
194 |
195 | If you plan to use Amazon Neptune as your graph database backend, install with the Amazon Neptune extra:
196 |
197 | ```bash
198 | pip install graphiti-core[neptune]
199 |
200 | # or with uv
201 | uv add graphiti-core[neptune]
202 | ```
203 |
204 | ### You can also install optional LLM providers as extras:
205 |
206 | ```bash
207 | # Install with Anthropic support
208 | pip install graphiti-core[anthropic]
209 |
210 | # Install with Groq support
211 | pip install graphiti-core[groq]
212 |
213 | # Install with Google Gemini support
214 | pip install graphiti-core[google-genai]
215 |
216 | # Install with multiple providers
217 | pip install graphiti-core[anthropic,groq,google-genai]
218 |
219 | # Install with FalkorDB and LLM providers
220 | pip install graphiti-core[falkordb,anthropic,google-genai]
221 |
222 | # Install with Amazon Neptune
223 | pip install graphiti-core[neptune]
224 | ```
225 |
226 | ## Default to Low Concurrency; LLM Provider 429 Rate Limit Errors
227 |
228 | Graphiti's ingestion pipelines are designed for high concurrency. By default, concurrency is set low to avoid LLM
229 | Provider 429 Rate Limit Errors. If you find Graphiti slow, please increase concurrency as described below.
230 |
231 | Concurrency controlled by the `SEMAPHORE_LIMIT` environment variable. By default, `SEMAPHORE_LIMIT` is set to `10`
232 | concurrent operations to help prevent `429` rate limit errors from your LLM provider. If you encounter such errors, try
233 | lowering this value.
234 |
235 | If your LLM provider allows higher throughput, you can increase `SEMAPHORE_LIMIT` to boost episode ingestion
236 | performance.
237 |
238 | ## Quick Start
239 |
240 | > [!IMPORTANT]
241 | > Graphiti defaults to using OpenAI for LLM inference and embedding. Ensure that an `OPENAI_API_KEY` is set in your
242 | > environment.
243 | > Support for Anthropic and Groq LLM inferences is available, too. Other LLM providers may be supported via OpenAI
244 | > compatible APIs.
245 |
246 | For a complete working example, see the [Quickstart Example](./examples/quickstart/README.md) in the examples directory.
247 | The quickstart demonstrates:
248 |
249 | 1. Connecting to a Neo4j, Amazon Neptune, FalkorDB, or Kuzu database
250 | 2. Initializing Graphiti indices and constraints
251 | 3. Adding episodes to the graph (both text and structured JSON)
252 | 4. Searching for relationships (edges) using hybrid search
253 | 5. Reranking search results using graph distance
254 | 6. Searching for nodes using predefined search recipes
255 |
256 | The example is fully documented with clear explanations of each functionality and includes a comprehensive README with
257 | setup instructions and next steps.
258 |
259 | ### Running with Docker Compose
260 |
261 | You can use Docker Compose to quickly start the required services:
262 |
263 | - **Neo4j Docker:**
264 | ```sh
265 | docker compose up
266 | ```
267 | This will start the Neo4j Docker service and related components.
268 |
269 | - **FalkorDB Docker:**
270 | ```sh
271 | docker compose --profile falkordb up
272 | ```
273 | This will start the FalkorDB Docker service and related components.
274 |
275 | ## MCP Server
276 |
277 | The `mcp_server` directory contains a Model Context Protocol (MCP) server implementation for Graphiti. This server
278 | allows AI assistants to interact with Graphiti's knowledge graph capabilities through the MCP protocol.
279 |
280 | Key features of the MCP server include:
281 |
282 | - Episode management (add, retrieve, delete)
283 | - Entity management and relationship handling
284 | - Semantic and hybrid search capabilities
285 | - Group management for organizing related data
286 | - Graph maintenance operations
287 |
288 | The MCP server can be deployed using Docker with Neo4j, making it easy to integrate Graphiti into your AI assistant
289 | workflows.
290 |
291 | For detailed setup instructions and usage examples, see the [MCP server README](./mcp_server/README.md).
292 |
293 | ## REST Service
294 |
295 | The `server` directory contains an API service for interacting with the Graphiti API. It is built using FastAPI.
296 |
297 | Please see the [server README](./server/README.md) for more information.
298 |
299 | ## Optional Environment Variables
300 |
301 | In addition to the Neo4j and OpenAi-compatible credentials, Graphiti also has a few optional environment variables.
302 | If you are using one of our supported models, such as Anthropic or Voyage models, the necessary environment variables
303 | must be set.
304 |
305 | ### Database Configuration
306 |
307 | Database names are configured directly in the driver constructors:
308 |
309 | - **Neo4j**: Database name defaults to `neo4j` (hardcoded in Neo4jDriver)
310 | - **FalkorDB**: Database name defaults to `default_db` (hardcoded in FalkorDriver)
311 |
312 | As of v0.17.0, if you need to customize your database configuration, you can instantiate a database driver and pass it
313 | to the Graphiti constructor using the `graph_driver` parameter.
314 |
315 | #### Neo4j with Custom Database Name
316 |
317 | ```python
318 | from graphiti_core import Graphiti
319 | from graphiti_core.driver.neo4j_driver import Neo4jDriver
320 |
321 | # Create a Neo4j driver with custom database name
322 | driver = Neo4jDriver(
323 | uri="bolt://localhost:7687",
324 | user="neo4j",
325 | password="password",
326 | database="my_custom_database" # Custom database name
327 | )
328 |
329 | # Pass the driver to Graphiti
330 | graphiti = Graphiti(graph_driver=driver)
331 | ```
332 |
333 | #### FalkorDB with Custom Database Name
334 |
335 | ```python
336 | from graphiti_core import Graphiti
337 | from graphiti_core.driver.falkordb_driver import FalkorDriver
338 |
339 | # Create a FalkorDB driver with custom database name
340 | driver = FalkorDriver(
341 | host="localhost",
342 | port=6379,
343 | username="falkor_user", # Optional
344 | password="falkor_password", # Optional
345 | database="my_custom_graph" # Custom database name
346 | )
347 |
348 | # Pass the driver to Graphiti
349 | graphiti = Graphiti(graph_driver=driver)
350 | ```
351 |
352 | #### Kuzu
353 |
354 | ```python
355 | from graphiti_core import Graphiti
356 | from graphiti_core.driver.kuzu_driver import KuzuDriver
357 |
358 | # Create a Kuzu driver
359 | driver = KuzuDriver(db="/tmp/graphiti.kuzu")
360 |
361 | # Pass the driver to Graphiti
362 | graphiti = Graphiti(graph_driver=driver)
363 | ```
364 |
365 | #### Amazon Neptune
366 |
367 | ```python
368 | from graphiti_core import Graphiti
369 | from graphiti_core.driver.neptune_driver import NeptuneDriver
370 |
371 | # Create a FalkorDB driver with custom database name
372 | driver = NeptuneDriver(
373 | host= < NEPTUNE
374 | ENDPOINT >,
375 | aoss_host = < Amazon
376 | OpenSearch
377 | Serverless
378 | Host >,
379 | port = < PORT > # Optional, defaults to 8182,
380 | aoss_port = < PORT > # Optional, defaults to 443
381 | )
382 |
383 | driver = NeptuneDriver(host=neptune_uri, aoss_host=aoss_host, port=neptune_port)
384 |
385 | # Pass the driver to Graphiti
386 | graphiti = Graphiti(graph_driver=driver)
387 | ```
388 |
389 | ## Using Graphiti with Azure OpenAI
390 |
391 | Graphiti supports Azure OpenAI for both LLM inference and embeddings using Azure's OpenAI v1 API compatibility layer.
392 |
393 | ### Quick Start
394 |
395 | ```python
396 | from openai import AsyncOpenAI
397 | from graphiti_core import Graphiti
398 | from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
399 | from graphiti_core.llm_client.config import LLMConfig
400 | from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
401 |
402 | # Initialize Azure OpenAI client using the standard OpenAI client
403 | # with Azure's v1 API endpoint
404 | azure_client = AsyncOpenAI(
405 | base_url="https://your-resource-name.openai.azure.com/openai/v1/",
406 | api_key="your-api-key",
407 | )
408 |
409 | # Create LLM and Embedder clients
410 | llm_client = AzureOpenAILLMClient(
411 | azure_client=azure_client,
412 | config=LLMConfig(model="gpt-5-mini", small_model="gpt-5-mini") # Your Azure deployment name
413 | )
414 | embedder_client = AzureOpenAIEmbedderClient(
415 | azure_client=azure_client,
416 | model="text-embedding-3-small" # Your Azure embedding deployment name
417 | )
418 |
419 | # Initialize Graphiti with Azure OpenAI clients
420 | graphiti = Graphiti(
421 | "bolt://localhost:7687",
422 | "neo4j",
423 | "password",
424 | llm_client=llm_client,
425 | embedder=embedder_client,
426 | )
427 |
428 | # Now you can use Graphiti with Azure OpenAI
429 | ```
430 |
431 | **Key Points:**
432 | - Use the standard `AsyncOpenAI` client with Azure's v1 API endpoint format: `https://your-resource-name.openai.azure.com/openai/v1/`
433 | - The deployment names (e.g., `gpt-5-mini`, `text-embedding-3-small`) should match your Azure OpenAI deployment names
434 | - See `examples/azure-openai/` for a complete working example
435 |
436 | Make sure to replace the placeholder values with your actual Azure OpenAI credentials and deployment names.
437 |
438 | ## Using Graphiti with Google Gemini
439 |
440 | Graphiti supports Google's Gemini models for LLM inference, embeddings, and cross-encoding/reranking. To use Gemini,
441 | you'll need to configure the LLM client, embedder, and the cross-encoder with your Google API key.
442 |
443 | Install Graphiti:
444 |
445 | ```bash
446 | uv add "graphiti-core[google-genai]"
447 |
448 | # or
449 |
450 | pip install "graphiti-core[google-genai]"
451 | ```
452 |
453 | ```python
454 | from graphiti_core import Graphiti
455 | from graphiti_core.llm_client.gemini_client import GeminiClient, LLMConfig
456 | from graphiti_core.embedder.gemini import GeminiEmbedder, GeminiEmbedderConfig
457 | from graphiti_core.cross_encoder.gemini_reranker_client import GeminiRerankerClient
458 |
459 | # Google API key configuration
460 | api_key = "<your-google-api-key>"
461 |
462 | # Initialize Graphiti with Gemini clients
463 | graphiti = Graphiti(
464 | "bolt://localhost:7687",
465 | "neo4j",
466 | "password",
467 | llm_client=GeminiClient(
468 | config=LLMConfig(
469 | api_key=api_key,
470 | model="gemini-2.0-flash"
471 | )
472 | ),
473 | embedder=GeminiEmbedder(
474 | config=GeminiEmbedderConfig(
475 | api_key=api_key,
476 | embedding_model="embedding-001"
477 | )
478 | ),
479 | cross_encoder=GeminiRerankerClient(
480 | config=LLMConfig(
481 | api_key=api_key,
482 | model="gemini-2.5-flash-lite"
483 | )
484 | )
485 | )
486 |
487 | # Now you can use Graphiti with Google Gemini for all components
488 | ```
489 |
490 | The Gemini reranker uses the `gemini-2.5-flash-lite` model by default, which is optimized for
491 | cost-effective and low-latency classification tasks. It uses the same boolean classification approach as the OpenAI
492 | reranker, leveraging Gemini's log probabilities feature to rank passage relevance.
493 |
494 | ## Using Graphiti with Ollama (Local LLM)
495 |
496 | Graphiti supports Ollama for running local LLMs and embedding models via Ollama's OpenAI-compatible API. This is ideal
497 | for privacy-focused applications or when you want to avoid API costs.
498 |
499 | **Note:** Use `OpenAIGenericClient` (not `OpenAIClient`) for Ollama and other OpenAI-compatible providers like LM Studio. The `OpenAIGenericClient` is optimized for local models with a higher default max token limit (16K vs 8K) and full support for structured outputs.
500 |
501 | Install the models:
502 |
503 | ```bash
504 | ollama pull deepseek-r1:7b # LLM
505 | ollama pull nomic-embed-text # embeddings
506 | ```
507 |
508 | ```python
509 | from graphiti_core import Graphiti
510 | from graphiti_core.llm_client.config import LLMConfig
511 | from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
512 | from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
513 | from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient
514 |
515 | # Configure Ollama LLM client
516 | llm_config = LLMConfig(
517 | api_key="ollama", # Ollama doesn't require a real API key, but some placeholder is needed
518 | model="deepseek-r1:7b",
519 | small_model="deepseek-r1:7b",
520 | base_url="http://localhost:11434/v1", # Ollama's OpenAI-compatible endpoint
521 | )
522 |
523 | llm_client = OpenAIGenericClient(config=llm_config)
524 |
525 | # Initialize Graphiti with Ollama clients
526 | graphiti = Graphiti(
527 | "bolt://localhost:7687",
528 | "neo4j",
529 | "password",
530 | llm_client=llm_client,
531 | embedder=OpenAIEmbedder(
532 | config=OpenAIEmbedderConfig(
533 | api_key="ollama", # Placeholder API key
534 | embedding_model="nomic-embed-text",
535 | embedding_dim=768,
536 | base_url="http://localhost:11434/v1",
537 | )
538 | ),
539 | cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config),
540 | )
541 |
542 | # Now you can use Graphiti with local Ollama models
543 | ```
544 |
545 | Ensure Ollama is running (`ollama serve`) and that you have pulled the models you want to use.
546 |
547 | ## Documentation
548 |
549 | - [Guides and API documentation](https://help.getzep.com/graphiti).
550 | - [Quick Start](https://help.getzep.com/graphiti/graphiti/quick-start)
551 | - [Building an agent with LangChain's LangGraph and Graphiti](https://help.getzep.com/graphiti/integrations/lang-graph-agent)
552 |
553 | ## Telemetry
554 |
555 | Graphiti collects anonymous usage statistics to help us understand how the framework is being used and improve it for
556 | everyone. We believe transparency is important, so here's exactly what we collect and why.
557 |
558 | ### What We Collect
559 |
560 | When you initialize a Graphiti instance, we collect:
561 |
562 | - **Anonymous identifier**: A randomly generated UUID stored locally in `~/.cache/graphiti/telemetry_anon_id`
563 | - **System information**: Operating system, Python version, and system architecture
564 | - **Graphiti version**: The version you're using
565 | - **Configuration choices**:
566 | - LLM provider type (OpenAI, Azure, Anthropic, etc.)
567 | - Database backend (Neo4j, FalkorDB, Kuzu, Amazon Neptune Database or Neptune Analytics)
568 | - Embedder provider (OpenAI, Azure, Voyage, etc.)
569 |
570 | ### What We Don't Collect
571 |
572 | We are committed to protecting your privacy. We **never** collect:
573 |
574 | - Personal information or identifiers
575 | - API keys or credentials
576 | - Your actual data, queries, or graph content
577 | - IP addresses or hostnames
578 | - File paths or system-specific information
579 | - Any content from your episodes, nodes, or edges
580 |
581 | ### Why We Collect This Data
582 |
583 | This information helps us:
584 |
585 | - Understand which configurations are most popular to prioritize support and testing
586 | - Identify which LLM and database providers to focus development efforts on
587 | - Track adoption patterns to guide our roadmap
588 | - Ensure compatibility across different Python versions and operating systems
589 |
590 | By sharing this anonymous information, you help us make Graphiti better for everyone in the community.
591 |
592 | ### View the Telemetry Code
593 |
594 | The Telemetry code [may be found here](graphiti_core/telemetry/telemetry.py).
595 |
596 | ### How to Disable Telemetry
597 |
598 | Telemetry is **opt-out** and can be disabled at any time. To disable telemetry collection:
599 |
600 | **Option 1: Environment Variable**
601 |
602 | ```bash
603 | export GRAPHITI_TELEMETRY_ENABLED=false
604 | ```
605 |
606 | **Option 2: Set in your shell profile**
607 |
608 | ```bash
609 | # For bash users (~/.bashrc or ~/.bash_profile)
610 | echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.bashrc
611 |
612 | # For zsh users (~/.zshrc)
613 | echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.zshrc
614 | ```
615 |
616 | **Option 3: Set for a specific Python session**
617 |
618 | ```python
619 | import os
620 |
621 | os.environ['GRAPHITI_TELEMETRY_ENABLED'] = 'false'
622 |
623 | # Then initialize Graphiti as usual
624 | from graphiti_core import Graphiti
625 |
626 | graphiti = Graphiti(...)
627 | ```
628 |
629 | Telemetry is automatically disabled during test runs (when `pytest` is detected).
630 |
631 | ### Technical Details
632 |
633 | - Telemetry uses PostHog for anonymous analytics collection
634 | - All telemetry operations are designed to fail silently - they will never interrupt your application or affect Graphiti
635 | functionality
636 | - The anonymous ID is stored locally and is not tied to any personal information
637 |
638 | ## Status and Roadmap
639 |
640 | Graphiti is under active development. We aim to maintain API stability while working on:
641 |
642 | - [x] Supporting custom graph schemas:
643 | - Allow developers to provide their own defined node and edge classes when ingesting episodes
644 | - Enable more flexible knowledge representation tailored to specific use cases
645 | - [x] Enhancing retrieval capabilities with more robust and configurable options
646 | - [x] Graphiti MCP Server
647 | - [ ] Expanding test coverage to ensure reliability and catch edge cases
648 |
649 | ## Contributing
650 |
651 | We encourage and appreciate all forms of contributions, whether it's code, documentation, addressing GitHub Issues, or
652 | answering questions in the Graphiti Discord channel. For detailed guidelines on code contributions, please refer
653 | to [CONTRIBUTING](CONTRIBUTING.md).
654 |
655 | ## Support
656 |
657 | Join the [Zep Discord server](https://discord.com/invite/W8Kw6bsgXQ) and make your way to the **#Graphiti** channel!
658 |
```
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
```markdown
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | |---------|--------------------|
10 | | 0.x | :white_check_mark: |
11 |
12 |
13 | ## Reporting a Vulnerability
14 |
15 | Please use GitHub's Private Vulnerability Reporting mechanism found in the Security section of this repo.
16 |
```
--------------------------------------------------------------------------------
/AGENTS.md:
--------------------------------------------------------------------------------
```markdown
1 | # Repository Guidelines
2 |
3 | ## Project Structure & Module Organization
4 | Graphiti's core library lives under `graphiti_core/`, split into domain modules such as `nodes.py`, `edges.py`, `models/`, and `search/` for retrieval pipelines. Service adapters and API glue reside in `server/graph_service/`, while the MCP integration lives in `mcp_server/`. Shared assets and collateral sit in `images/` and `examples/`. Tests cover the package via `tests/`, with configuration in `conftest.py`, `pytest.ini`, and Docker compose files for optional services. Tooling manifests live at the repo root, including `pyproject.toml`, `Makefile`, and deployment compose files.
5 |
6 | ## Build, Test, and Development Commands
7 | - `uv sync --extra dev`: install the dev environment declared in `pyproject.toml`.
8 | - `make format`: run `ruff` to sort imports and apply the canonical formatter.
9 | - `make lint`: execute `ruff` plus `pyright` type checks against `graphiti_core`.
10 | - `make test`: run the full `pytest` suite (`uv run pytest`).
11 | - `uv run pytest tests/path/test_file.py`: target a specific module or test selection.
12 | - `docker-compose -f docker-compose.test.yml up`: provision local graph/search dependencies for integration flows.
13 |
14 | ## Coding Style & Naming Conventions
15 | Python code uses 4-space indentation, 100-character lines, and prefers single quotes as configured in `pyproject.toml`. Modules, files, and functions stay snake_case; Pydantic models in `graphiti_core/models` use PascalCase with explicit type hints. Keep side-effectful code inside drivers or adapters (`graphiti_core/driver`, `graphiti_core/utils`) and rely on pure helpers elsewhere. Run `make format` before committing to normalize imports and docstring formatting.
16 |
17 | ## Testing Guidelines
18 | Author tests alongside features under `tests/`, naming files `test_<feature>.py` and functions `test_<behavior>`. Use `@pytest.mark.integration` for database-reliant scenarios so CI can gate them. Reproduce regressions with a failing test first and validate fixes via `uv run pytest -k "pattern"`. Start required backing services through `docker-compose.test.yml` when running integration suites locally.
19 |
20 | ## Commit & Pull Request Guidelines
21 | Commits use an imperative, present-tense summary (for example, `add async cache invalidation`) optionally suffixed with the PR number as seen in history (`(#927)`). Squash fixups and keep unrelated changes isolated. Pull requests should include: a concise description, linked tracking issue, notes about schema or API impacts, and screenshots or logs when behavior changes. Confirm `make lint` and `make test` pass locally, and update docs or examples when public interfaces shift.
22 |
```
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
```markdown
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | [email protected].
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
```
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
```markdown
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Project Overview
6 |
7 | Graphiti is a Python framework for building temporally-aware knowledge graphs designed for AI agents. It enables real-time incremental updates to knowledge graphs without batch recomputation, making it suitable for dynamic environments.
8 |
9 | Key features:
10 |
11 | - Bi-temporal data model with explicit tracking of event occurrence times
12 | - Hybrid retrieval combining semantic embeddings, keyword search (BM25), and graph traversal
13 | - Support for custom entity definitions via Pydantic models
14 | - Integration with Neo4j and FalkorDB as graph storage backends
15 | - Optional OpenTelemetry distributed tracing support
16 |
17 | ## Development Commands
18 |
19 | ### Main Development Commands (run from project root)
20 |
21 | ```bash
22 | # Install dependencies
23 | uv sync --extra dev
24 |
25 | # Format code (ruff import sorting + formatting)
26 | make format
27 |
28 | # Lint code (ruff + pyright type checking)
29 | make lint
30 |
31 | # Run tests
32 | make test
33 |
34 | # Run all checks (format, lint, test)
35 | make check
36 | ```
37 |
38 | ### Server Development (run from server/ directory)
39 |
40 | ```bash
41 | cd server/
42 | # Install server dependencies
43 | uv sync --extra dev
44 |
45 | # Run server in development mode
46 | uvicorn graph_service.main:app --reload
47 |
48 | # Format, lint, test server code
49 | make format
50 | make lint
51 | make test
52 | ```
53 |
54 | ### MCP Server Development (run from mcp_server/ directory)
55 |
56 | ```bash
57 | cd mcp_server/
58 | # Install MCP server dependencies
59 | uv sync
60 |
61 | # Run with Docker Compose
62 | docker-compose up
63 | ```
64 |
65 | ## Code Architecture
66 |
67 | ### Core Library (`graphiti_core/`)
68 |
69 | - **Main Entry Point**: `graphiti.py` - Contains the main `Graphiti` class that orchestrates all functionality
70 | - **Graph Storage**: `driver/` - Database drivers for Neo4j and FalkorDB
71 | - **LLM Integration**: `llm_client/` - Clients for OpenAI, Anthropic, Gemini, Groq
72 | - **Embeddings**: `embedder/` - Embedding clients for various providers
73 | - **Graph Elements**: `nodes.py`, `edges.py` - Core graph data structures
74 | - **Search**: `search/` - Hybrid search implementation with configurable strategies
75 | - **Prompts**: `prompts/` - LLM prompts for entity extraction, deduplication, summarization
76 | - **Utilities**: `utils/` - Maintenance operations, bulk processing, datetime handling
77 |
78 | ### Server (`server/`)
79 |
80 | - **FastAPI Service**: `graph_service/main.py` - REST API server
81 | - **Routers**: `routers/` - API endpoints for ingestion and retrieval
82 | - **DTOs**: `dto/` - Data transfer objects for API contracts
83 |
84 | ### MCP Server (`mcp_server/`)
85 |
86 | - **MCP Implementation**: `graphiti_mcp_server.py` - Model Context Protocol server for AI assistants
87 | - **Docker Support**: Containerized deployment with Neo4j
88 |
89 | ## Testing
90 |
91 | - **Unit Tests**: `tests/` - Comprehensive test suite using pytest
92 | - **Integration Tests**: Tests marked with `_int` suffix require database connections
93 | - **Evaluation**: `tests/evals/` - End-to-end evaluation scripts
94 |
95 | ## Configuration
96 |
97 | ### Environment Variables
98 |
99 | - `OPENAI_API_KEY` - Required for LLM inference and embeddings
100 | - `USE_PARALLEL_RUNTIME` - Optional boolean for Neo4j parallel runtime (enterprise only)
101 | - Provider-specific keys: `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `GROQ_API_KEY`, `VOYAGE_API_KEY`
102 |
103 | ### Database Setup
104 |
105 | - **Neo4j**: Version 5.26+ required, available via Neo4j Desktop
106 | - Database name defaults to `neo4j` (hardcoded in Neo4jDriver)
107 | - Override by passing `database` parameter to driver constructor
108 | - **FalkorDB**: Version 1.1.2+ as alternative backend
109 | - Database name defaults to `default_db` (hardcoded in FalkorDriver)
110 | - Override by passing `database` parameter to driver constructor
111 |
112 | ## Development Guidelines
113 |
114 | ### Code Style
115 |
116 | - Use Ruff for formatting and linting (configured in pyproject.toml)
117 | - Line length: 100 characters
118 | - Quote style: single quotes
119 | - Type checking with Pyright is enforced
120 | - Main project uses `typeCheckingMode = "basic"`, server uses `typeCheckingMode = "standard"`
121 |
122 | ### Testing Requirements
123 |
124 | - Run tests with `make test` or `pytest`
125 | - Integration tests require database connections and are marked with `_int` suffix
126 | - Use `pytest-xdist` for parallel test execution
127 | - Run specific test files: `pytest tests/test_specific_file.py`
128 | - Run specific test methods: `pytest tests/test_file.py::test_method_name`
129 | - Run only integration tests: `pytest tests/ -k "_int"`
130 | - Run only unit tests: `pytest tests/ -k "not _int"`
131 |
132 | ### LLM Provider Support
133 |
134 | The codebase supports multiple LLM providers but works best with services supporting structured output (OpenAI, Gemini). Other providers may cause schema validation issues, especially with smaller models.
135 |
136 | #### Current LLM Models (as of November 2025)
137 |
138 | **OpenAI Models:**
139 | - **GPT-5 Family** (Reasoning models, require temperature=0):
140 | - `gpt-5-mini` - Fast reasoning model
141 | - `gpt-5-nano` - Smallest reasoning model
142 | - **GPT-4.1 Family** (Standard models):
143 | - `gpt-4.1` - Full capability model
144 | - `gpt-4.1-mini` - Efficient model for most tasks
145 | - `gpt-4.1-nano` - Lightweight model
146 | - **Legacy Models** (Still supported):
147 | - `gpt-4o` - Previous generation flagship
148 | - `gpt-4o-mini` - Previous generation efficient
149 |
150 | **Anthropic Models:**
151 | - **Claude 4.5 Family** (Latest):
152 | - `claude-sonnet-4-5-latest` - Flagship model, auto-updates
153 | - `claude-sonnet-4-5-20250929` - Pinned Sonnet version from September 2025
154 | - `claude-haiku-4-5-latest` - Fast model, auto-updates
155 | - **Claude 3.7 Family**:
156 | - `claude-3-7-sonnet-latest` - Auto-updates
157 | - `claude-3-7-sonnet-20250219` - Pinned version from February 2025
158 | - **Claude 3.5 Family**:
159 | - `claude-3-5-sonnet-latest` - Auto-updates
160 | - `claude-3-5-sonnet-20241022` - Pinned version from October 2024
161 | - `claude-3-5-haiku-latest` - Fast model
162 |
163 | **Google Gemini Models:**
164 | - **Gemini 2.5 Family** (Latest):
165 | - `gemini-2.5-pro` - Flagship reasoning and multimodal
166 | - `gemini-2.5-flash` - Fast, efficient
167 | - **Gemini 2.0 Family**:
168 | - `gemini-2.0-flash` - Experimental fast model
169 | - **Gemini 1.5 Family** (Stable):
170 | - `gemini-1.5-pro` - Production-stable flagship
171 | - `gemini-1.5-flash` - Production-stable efficient
172 |
173 | **Note**: Model names like `gpt-5-mini`, `gpt-4.1`, and `gpt-4.1-mini` used in this codebase are valid OpenAI model identifiers. The GPT-5 family are reasoning models that require `temperature=0` (automatically handled in the code).
174 |
175 | ### MCP Server Usage Guidelines
176 |
177 | When working with the MCP server, follow the patterns established in `mcp_server/cursor_rules.md`:
178 |
179 | - Always search for existing knowledge before adding new information
180 | - Use specific entity type filters (`Preference`, `Procedure`, `Requirement`)
181 | - Store new information immediately using `add_memory`
182 | - Follow discovered procedures and respect established preferences
```
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
```markdown
1 | # Contributing to Graphiti
2 |
3 | We're thrilled you're interested in contributing to Graphiti! As firm believers in the power of open source collaboration, we're committed to building not just a tool, but a vibrant community where developers of all experience levels can make meaningful contributions.
4 |
5 | When I first joined this project, I was overwhelmed trying to figure out where to start. Someone eventually pointed me to a random "good first issue," but I later discovered there were multiple ways I could have contributed that would have better matched my skills and interests.
6 |
7 | We've restructured our contribution paths to solve this problem:
8 |
9 | # Four Ways to Get Involved
10 |
11 | ### Pick Up Existing Issues
12 |
13 | Our developers regularly tag issues with "help wanted" and "good first issue." These are pre-vetted tasks with clear scope and someone ready to help you if you get stuck.
14 |
15 | ### Create Your Own Tickets
16 |
17 | See something that needs fixing? Have an idea for an improvement? You don't need permission to identify problems. The people closest to the pain are often best positioned to describe the solution.
18 |
19 | For **feature requests**, tell us the story of what you're trying to accomplish. What are you working on? What's getting in your way? What would make your life easier? Submit these through our [GitHub issue tracker](https://github.com/getzep/graphiti/issues) with a "Feature Request" label.
20 |
21 | For **bug reports**, we need enough context to reproduce the problem. Use the [GitHub issue tracker](https://github.com/getzep/graphiti/issues) and include:
22 |
23 | - A clear title that summarizes the specific problem
24 | - What you were trying to do when you encountered the bug
25 | - What you expected to happen
26 | - What actually happened
27 | - A code sample or test case that demonstrates the issue
28 |
29 | ### Share Your Use Cases
30 |
31 | Sometimes the most valuable contribution isn't code. If you're using our project in an interesting way, add it to the [examples](https://github.com/getzep/graphiti/tree/main/examples) folder. This helps others discover new possibilities and counts as a meaningful contribution. We regularly feature compelling examples in our blog posts and videos - your work might be showcased to the broader community!
32 |
33 | ### Help Others in Discord
34 |
35 | Join our [Discord server](https://discord.com/invite/W8Kw6bsgXQ) community and pitch in at the helpdesk. Answering questions and helping troubleshoot issues is an incredibly valuable contribution that benefits everyone. The knowledge you share today saves someone hours of frustration tomorrow.
36 |
37 | ## What happens next?
38 |
39 | ### Notes for Large Changes
40 | > Please keep the changes as concise as possible. For major architectural changes (>500 LOC), we would expect a GitHub issue (RFC) discussing the technical design and justification. Otherwise, we will tag it with rfc-required and might not go through the PR.
41 |
42 | Once you've found an issue tagged with "good first issue" or "help wanted," or prepared an example to share, here's how to turn that into a contribution:
43 |
44 | 1. Share your approach in the issue discussion or [Discord](https://discord.com/invite/W8Kw6bsgXQ) before diving deep into code. This helps ensure your solution adheres to the architecture of Graphiti from the start and saves you from potential rework.
45 |
46 | 2. Fork the repo, make your changes in a branch, and submit a PR. We've included more detailed technical instructions below; be open to feedback during review.
47 |
48 | ## Setup
49 |
50 | 1. Fork the repository on GitHub.
51 | 2. Clone your fork locally:
52 | ```
53 | git clone https://github.com/getzep/graphiti
54 | cd graphiti
55 | ```
56 | 3. Set up your development environment:
57 |
58 | - Ensure you have Python 3.10+ installed.
59 | - Install uv: https://docs.astral.sh/uv/getting-started/installation/
60 | - Install project dependencies:
61 | ```
62 | make install
63 | ```
64 | - To run integration tests, set the appropriate environment variables
65 |
66 | ```
67 | export TEST_OPENAI_API_KEY=...
68 | export TEST_OPENAI_MODEL=...
69 | export TEST_ANTHROPIC_API_KEY=...
70 |
71 | # For Neo4j
72 | export TEST_URI=neo4j://...
73 | export TEST_USER=...
74 | export TEST_PASSWORD=...
75 | ```
76 |
77 | ## Making Changes
78 |
79 | 1. Create a new branch for your changes:
80 | ```
81 | git checkout -b your-branch-name
82 | ```
83 | 2. Make your changes in the codebase.
84 | 3. Write or update tests as necessary.
85 | 4. Run the tests to ensure they pass:
86 | ```
87 | make test
88 | ```
89 | 5. Format your code:
90 | ```
91 | make format
92 | ```
93 | 6. Run linting checks:
94 | ```
95 | make lint
96 | ```
97 |
98 | ## Submitting Changes
99 |
100 | 1. Commit your changes:
101 | ```
102 | git commit -m "Your detailed commit message"
103 | ```
104 | 2. Push to your fork:
105 | ```
106 | git push origin your-branch-name
107 | ```
108 | 3. Submit a pull request through the GitHub website to https://github.com/getzep/graphiti.
109 |
110 | ## Pull Request Guidelines
111 |
112 | - Provide a clear title and description of your changes.
113 | - Include any relevant issue numbers in the PR description.
114 | - Ensure all tests pass and there are no linting errors.
115 | - Update documentation if you're changing functionality.
116 |
117 | ## Code Style and Quality
118 |
119 | We use several tools to maintain code quality:
120 |
121 | - Ruff for linting and formatting
122 | - Pyright for static type checking
123 | - Pytest for testing
124 |
125 | Before submitting a pull request, please run:
126 |
127 | ```
128 | make check
129 | ```
130 |
131 | This command will format your code, run linting checks, and execute tests.
132 |
133 | ## Third-Party Integrations
134 |
135 | When contributing integrations for third-party services (LLM providers, embedding services, databases, etc.), please follow these patterns:
136 |
137 | ### Optional Dependencies
138 |
139 | All third-party integrations must be optional dependencies to keep the core library lightweight. Follow this pattern:
140 |
141 | 1. **Add to `pyproject.toml`**: Define your dependency as an optional extra AND include it in the dev extra:
142 | ```toml
143 | [project.optional-dependencies]
144 | your-service = ["your-package>=1.0.0"]
145 | dev = [
146 | # ... existing dev dependencies
147 | "your-package>=1.0.0", # Include all optional extras here
148 | # ... other dependencies
149 | ]
150 | ```
151 |
152 | 2. **Use TYPE_CHECKING pattern**: In your integration module, import dependencies conditionally:
153 | ```python
154 | from typing import TYPE_CHECKING
155 |
156 | if TYPE_CHECKING:
157 | import your_package
158 | from your_package import SomeType
159 | else:
160 | try:
161 | import your_package
162 | from your_package import SomeType
163 | except ImportError:
164 | raise ImportError(
165 | 'your-package is required for YourServiceClient. '
166 | 'Install it with: pip install graphiti-core[your-service]'
167 | ) from None
168 | ```
169 |
170 | 3. **Benefits of this pattern**:
171 | - Fast startup times (no import overhead during type checking)
172 | - Clear error messages with installation instructions
173 | - Proper type hints for development
174 | - Consistent user experience
175 |
176 | 4. **Do NOT**:
177 | - Add optional imports to `__init__.py` files
178 | - Use direct imports without error handling
179 | - Include optional dependencies in the main `dependencies` list
180 |
181 | ### Integration Structure
182 |
183 | - Place LLM clients in `graphiti_core/llm_client/`
184 | - Place embedding clients in `graphiti_core/embedder/`
185 | - Place database drivers in `graphiti_core/driver/`
186 | - Follow existing naming conventions (e.g., `your_service_client.py`)
187 |
188 | ### Testing
189 |
190 | - Add comprehensive tests in the appropriate `tests/` subdirectory
191 | - Mark integration tests with `_int` suffix if they require external services
192 | - Include both unit tests and integration tests where applicable
193 |
194 | # Questions?
195 |
196 | Stuck on a contribution or have a half-formed idea? Come say hello in our [Discord server](https://discord.com/invite/W8Kw6bsgXQ). Whether you're ready to contribute or just want to learn more, we're happy to have you! It's faster than GitHub issues and you'll find both maintainers and fellow contributors ready to help.
197 |
198 | Thank you for contributing to Graphiti!
199 |
```
--------------------------------------------------------------------------------
/graphiti_core/migrations/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/graphiti_core/models/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/graphiti_core/models/edges/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/graphiti_core/models/nodes/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/graphiti_core/search/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/graphiti_core/utils/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/src/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/src/config/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/src/models/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/src/services/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/src/utils/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/mcp_server/tests/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/server/graph_service/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/server/graph_service/routers/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/depot.json:
--------------------------------------------------------------------------------
```json
1 | {"id":"v9jv1mlpwc"}
2 |
```
--------------------------------------------------------------------------------
/examples/quickstart/requirements.txt:
--------------------------------------------------------------------------------
```
1 | graphiti-core
2 | python-dotenv>=1.0.0
```
--------------------------------------------------------------------------------
/tests/driver/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """Tests for database drivers."""
2 |
```
--------------------------------------------------------------------------------
/graphiti_core/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .graphiti import Graphiti
2 |
3 | __all__ = ['Graphiti']
4 |
```
--------------------------------------------------------------------------------
/graphiti_core/prompts/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .lib import prompt_library
2 | from .models import Message
3 |
4 | __all__ = ['prompt_library', 'Message']
5 |
```
--------------------------------------------------------------------------------
/tests/evals/pytest.ini:
--------------------------------------------------------------------------------
```
1 | [pytest]
2 | asyncio_default_fixture_loop_scope = function
3 | markers =
4 | integration: marks tests as integration tests
```
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
```
1 | [pytest]
2 | markers =
3 | integration: marks tests as integration tests
4 | asyncio_default_fixture_loop_scope = function
5 | asyncio_mode = auto
6 |
```
--------------------------------------------------------------------------------
/graphiti_core/embedder/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .client import EmbedderClient
2 | from .openai import OpenAIEmbedder, OpenAIEmbedderConfig
3 |
4 | __all__ = [
5 | 'EmbedderClient',
6 | 'OpenAIEmbedder',
7 | 'OpenAIEmbedderConfig',
8 | ]
9 |
```
--------------------------------------------------------------------------------
/graphiti_core/telemetry/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Telemetry module for Graphiti.
3 |
4 | This module provides anonymous usage analytics to help improve Graphiti.
5 | """
6 |
7 | from .telemetry import capture_event, is_telemetry_enabled
8 |
9 | __all__ = ['capture_event', 'is_telemetry_enabled']
10 |
```
--------------------------------------------------------------------------------
/examples/opentelemetry/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [project]
2 | name = "graphiti-otel-stdout-example"
3 | version = "0.1.0"
4 | requires-python = ">=3.10"
5 | dependencies = [
6 | "graphiti-core",
7 | "kuzu>=0.11.2",
8 | "opentelemetry-api>=1.20.0",
9 | "opentelemetry-sdk>=1.20.0",
10 | ]
11 |
12 | [tool.uv.sources]
13 | graphiti-core = { path = "../..", editable = true }
14 |
```
--------------------------------------------------------------------------------
/graphiti_core/utils/maintenance/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .edge_operations import build_episodic_edges, extract_edges
2 | from .graph_data_operations import clear_data, retrieve_episodes
3 | from .node_operations import extract_nodes
4 |
5 | __all__ = [
6 | 'extract_edges',
7 | 'build_episodic_edges',
8 | 'extract_nodes',
9 | 'clear_data',
10 | 'retrieve_episodes',
11 | ]
12 |
```
--------------------------------------------------------------------------------
/mcp_server/pytest.ini:
--------------------------------------------------------------------------------
```
1 | [pytest]
2 | # MCP Server specific pytest configuration
3 | testpaths = tests
4 | python_files = test_*.py
5 | python_classes = Test*
6 | python_functions = test_*
7 | addopts = -v --tb=short
8 | # Configure asyncio
9 | asyncio_mode = auto
10 | asyncio_default_fixture_loop_scope = function
11 | # Ignore warnings from dependencies
12 | filterwarnings =
13 | ignore::DeprecationWarning
14 | ignore::PendingDeprecationWarning
```
--------------------------------------------------------------------------------
/server/graph_service/dto/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .common import Message, Result
2 | from .ingest import AddEntityNodeRequest, AddMessagesRequest
3 | from .retrieve import FactResult, GetMemoryRequest, GetMemoryResponse, SearchQuery, SearchResults
4 |
5 | __all__ = [
6 | 'SearchQuery',
7 | 'Message',
8 | 'AddMessagesRequest',
9 | 'AddEntityNodeRequest',
10 | 'SearchResults',
11 | 'FactResult',
12 | 'Result',
13 | 'GetMemoryRequest',
14 | 'GetMemoryResponse',
15 | ]
16 |
```
--------------------------------------------------------------------------------
/.github/secret_scanning.yml:
--------------------------------------------------------------------------------
```yaml
1 | # Secret scanning configuration
2 | # This file excludes specific files/directories from secret scanning alerts
3 |
4 | paths-ignore:
5 | # PostHog public API key for anonymous telemetry
6 | # This is a public key intended for client-side use and safe to commit
7 | # Key: phc_UG6EcfDbuXz92neb3rMlQFDY0csxgMqRcIPWESqnSmo
8 | - "graphiti_core/telemetry/telemetry.py"
9 |
10 | # Example/test directories that may contain dummy credentials
11 | - "tests/**/fixtures/**"
```
--------------------------------------------------------------------------------
/mcp_server/config/mcp_config_stdio_example.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "mcpServers": {
3 | "graphiti": {
4 | "transport": "stdio",
5 | "command": "uv",
6 | "args": [
7 | "run",
8 | "/ABSOLUTE/PATH/TO/graphiti_mcp_server.py",
9 | "--transport",
10 | "stdio"
11 | ],
12 | "env": {
13 | "NEO4J_URI": "bolt://localhost:7687",
14 | "NEO4J_USER": "neo4j",
15 | "NEO4J_PASSWORD": "demodemo",
16 | "OPENAI_API_KEY": "${OPENAI_API_KEY}",
17 | "MODEL_NAME": "gpt-4.1-mini"
18 | }
19 | }
20 | }
21 | }
22 |
```
--------------------------------------------------------------------------------
/conftest.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import sys
3 |
4 | # This code adds the project root directory to the Python path, allowing imports to work correctly when running tests.
5 | # Without this file, you might encounter ModuleNotFoundError when trying to import modules from your project, especially when running tests.
6 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__))))
7 |
8 | from tests.helpers_test import graph_driver, mock_embedder
9 |
10 | __all__ = ['graph_driver', 'mock_embedder']
11 |
```
--------------------------------------------------------------------------------
/mcp_server/tests/conftest.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Pytest configuration for MCP server tests.
3 | This file prevents pytest from loading the parent project's conftest.py
4 | """
5 |
6 | import sys
7 | from pathlib import Path
8 |
9 | import pytest
10 |
11 | # Add src directory to Python path for imports
12 | src_path = Path(__file__).parent.parent / 'src'
13 | sys.path.insert(0, str(src_path))
14 |
15 | from config.schema import GraphitiConfig # noqa: E402
16 |
17 |
18 | @pytest.fixture
19 | def config():
20 | """Provide a default GraphitiConfig for tests."""
21 | return GraphitiConfig()
22 |
```
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Lint with Ruff
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 | branches: ["main"]
8 |
9 | jobs:
10 | ruff:
11 | environment: development
12 | runs-on: depot-ubuntu-22.04
13 | steps:
14 | - uses: actions/checkout@v4
15 | - name: Set up Python
16 | uses: actions/setup-python@v5
17 | with:
18 | python-version: "3.10"
19 | - name: Install dependencies
20 | run: |
21 | python -m pip install --upgrade pip
22 | pip install "ruff>0.1.7"
23 | - name: Run Ruff linting
24 | run: ruff check --output-format=github
25 |
```
--------------------------------------------------------------------------------
/server/graph_service/dto/ingest.py:
--------------------------------------------------------------------------------
```python
1 | from pydantic import BaseModel, Field
2 |
3 | from graph_service.dto.common import Message
4 |
5 |
6 | class AddMessagesRequest(BaseModel):
7 | group_id: str = Field(..., description='The group id of the messages to add')
8 | messages: list[Message] = Field(..., description='The messages to add')
9 |
10 |
11 | class AddEntityNodeRequest(BaseModel):
12 | uuid: str = Field(..., description='The uuid of the node to add')
13 | group_id: str = Field(..., description='The group id of the node to add')
14 | name: str = Field(..., description='The name of the node to add')
15 | summary: str = Field(default='', description='The summary of the node to add')
16 |
```
--------------------------------------------------------------------------------
/graphiti_core/driver/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from neo4j import Neo4jDriver
18 |
19 | __all__ = ['Neo4jDriver']
20 |
```
--------------------------------------------------------------------------------
/server/graph_service/config.py:
--------------------------------------------------------------------------------
```python
1 | from functools import lru_cache
2 | from typing import Annotated
3 |
4 | from fastapi import Depends
5 | from pydantic import Field
6 | from pydantic_settings import BaseSettings, SettingsConfigDict # type: ignore
7 |
8 |
9 | class Settings(BaseSettings):
10 | openai_api_key: str
11 | openai_base_url: str | None = Field(None)
12 | model_name: str | None = Field(None)
13 | embedding_model_name: str | None = Field(None)
14 | neo4j_uri: str
15 | neo4j_user: str
16 | neo4j_password: str
17 |
18 | model_config = SettingsConfigDict(env_file='.env', extra='ignore')
19 |
20 |
21 | @lru_cache
22 | def get_settings():
23 | return Settings() # type: ignore[call-arg]
24 |
25 |
26 | ZepEnvDep = Annotated[Settings, Depends(get_settings)]
27 |
```
--------------------------------------------------------------------------------
/mcp_server/main.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | Main entry point for Graphiti MCP Server
4 |
5 | This is a backwards-compatible wrapper around the original graphiti_mcp_server.py
6 | to maintain compatibility with existing deployment scripts and documentation.
7 |
8 | Usage:
9 | python main.py [args...]
10 |
11 | All arguments are passed through to the original server implementation.
12 | """
13 |
14 | import sys
15 | from pathlib import Path
16 |
17 | # Add src directory to Python path for imports
18 | src_path = Path(__file__).parent / 'src'
19 | sys.path.insert(0, str(src_path))
20 |
21 | # Import and run the original server
22 | if __name__ == '__main__':
23 | from graphiti_mcp_server import main
24 |
25 | # Pass all command line arguments to the original main function
26 | main()
27 |
```
--------------------------------------------------------------------------------
/server/graph_service/main.py:
--------------------------------------------------------------------------------
```python
1 | from contextlib import asynccontextmanager
2 |
3 | from fastapi import FastAPI
4 | from fastapi.responses import JSONResponse
5 |
6 | from graph_service.config import get_settings
7 | from graph_service.routers import ingest, retrieve
8 | from graph_service.zep_graphiti import initialize_graphiti
9 |
10 |
11 | @asynccontextmanager
12 | async def lifespan(_: FastAPI):
13 | settings = get_settings()
14 | await initialize_graphiti(settings)
15 | yield
16 | # Shutdown
17 | # No need to close Graphiti here, as it's handled per-request
18 |
19 |
20 | app = FastAPI(lifespan=lifespan)
21 |
22 |
23 | app.include_router(retrieve.router)
24 | app.include_router(ingest.router)
25 |
26 |
27 | @app.get('/healthcheck')
28 | async def healthcheck():
29 | return JSONResponse(content={'status': 'healthy'}, status_code=200)
30 |
```
--------------------------------------------------------------------------------
/graphiti_core/cross_encoder/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2025, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from .client import CrossEncoderClient
18 | from .openai_reranker_client import OpenAIRerankerClient
19 |
20 | __all__ = ['CrossEncoderClient', 'OpenAIRerankerClient']
21 |
```
--------------------------------------------------------------------------------
/mcp_server/src/models/response_types.py:
--------------------------------------------------------------------------------
```python
1 | """Response type definitions for Graphiti MCP Server."""
2 |
3 | from typing import Any
4 |
5 | from typing_extensions import TypedDict
6 |
7 |
8 | class ErrorResponse(TypedDict):
9 | error: str
10 |
11 |
12 | class SuccessResponse(TypedDict):
13 | message: str
14 |
15 |
16 | class NodeResult(TypedDict):
17 | uuid: str
18 | name: str
19 | labels: list[str]
20 | created_at: str | None
21 | summary: str | None
22 | group_id: str
23 | attributes: dict[str, Any]
24 |
25 |
26 | class NodeSearchResponse(TypedDict):
27 | message: str
28 | nodes: list[NodeResult]
29 |
30 |
31 | class FactSearchResponse(TypedDict):
32 | message: str
33 | facts: list[dict[str, Any]]
34 |
35 |
36 | class EpisodeSearchResponse(TypedDict):
37 | message: str
38 | episodes: list[dict[str, Any]]
39 |
40 |
41 | class StatusResponse(TypedDict):
42 | status: str
43 | message: str
44 |
```
--------------------------------------------------------------------------------
/graphiti_core/llm_client/__init__.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from .client import LLMClient
18 | from .config import LLMConfig
19 | from .errors import RateLimitError
20 | from .openai_client import OpenAIClient
21 |
22 | __all__ = ['LLMClient', 'OpenAIClient', 'LLMConfig', 'RateLimitError']
23 |
```
--------------------------------------------------------------------------------
/tests/embedder/embedder_fixtures.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 |
18 | def create_embedding_values(multiplier: float = 0.1, dimension: int = 1536) -> list[float]:
19 | """Create embedding values with the specified multiplier and dimension."""
20 | return [multiplier] * dimension
21 |
```
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
```yaml
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "pip" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 | - package-ecosystem: "pip"
13 | directory: "/server" # Location of server package manifests
14 | schedule:
15 | interval: "weekly"
16 | - package-ecosystem: "pip"
17 | directory: "/mcp_server" # Location of server package manifests
18 | schedule:
19 | interval: "weekly"
```
--------------------------------------------------------------------------------
/.github/workflows/ai-moderator.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: AI Moderator
2 | on:
3 | issues:
4 | types: [opened]
5 | issue_comment:
6 | types: [created]
7 | pull_request_review_comment:
8 | types: [created]
9 |
10 | jobs:
11 | spam-detection:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | issues: write
15 | pull-requests: write
16 | models: read
17 | contents: read
18 | steps:
19 | - uses: actions/checkout@v4
20 | - uses: github/ai-moderator@v1
21 | with:
22 | token: ${{ secrets.GITHUB_TOKEN }}
23 | spam-label: 'spam'
24 | ai-label: 'ai-generated'
25 | minimize-detected-comments: true
26 | # Built-in prompt configuration (all enabled by default)
27 | enable-spam-detection: true
28 | enable-link-spam-detection: true
29 | enable-ai-detection: true
30 | # custom-prompt-path: '.github/prompts/my-custom.prompt.yml' # Optional
```
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
```markdown
1 | ## Summary
2 | Brief description of the changes in this PR.
3 |
4 | ## Type of Change
5 | - [ ] Bug fix
6 | - [ ] New feature
7 | - [ ] Performance improvement
8 | - [ ] Documentation/Tests
9 |
10 | ## Objective
11 | **For new features and performance improvements:** Clearly describe the objective and rationale for this change.
12 |
13 | ## Testing
14 | - [ ] Unit tests added/updated
15 | - [ ] Integration tests added/updated
16 | - [ ] All existing tests pass
17 |
18 | ## Breaking Changes
19 | - [ ] This PR contains breaking changes
20 |
21 | If this is a breaking change, describe:
22 | - What functionality is affected
23 | - Migration path for existing users
24 |
25 | ## Checklist
26 | - [ ] Code follows project style guidelines (`make lint` passes)
27 | - [ ] Self-review completed
28 | - [ ] Documentation updated where necessary
29 | - [ ] No secrets or sensitive information committed
30 |
31 | ## Related Issues
32 | Closes #[issue number]
```
--------------------------------------------------------------------------------
/ellipsis.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # See https://docs.ellipsis.dev for all available configurations.
2 |
3 | version: 1.3
4 |
5 | pr_address_comments:
6 | delivery: "new_commit"
7 | pr_review:
8 | auto_review_enabled: true # enable auto-review of PRs
9 | auto_summarize_pr: true # enable auto-summary of PRs
10 | confidence_threshold: 0.8 # Threshold for how confident Ellipsis needs to be in order to leave a comment, in range [0.0-1.0]
11 | rules: # customize behavior
12 | - "Ensure the copyright notice is present as the header of all Python files"
13 | - "Ensure code is idiomatic"
14 | - "Code should be DRY (Don't Repeat Yourself)"
15 | - "Extremely Complicated Code Needs Comments"
16 | - "Use Descriptive Variable and Constant Names"
17 | - "Follow the Single Responsibility Principle"
18 | - "Function and Method Naming Should Follow Consistent Patterns"
19 | - "There should no secrets or credentials in the code"
20 | - "Don't log sensitive data"
```
--------------------------------------------------------------------------------
/graphiti_core/prompts/models.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from collections.abc import Callable
18 | from typing import Any, Protocol
19 |
20 | from pydantic import BaseModel
21 |
22 |
23 | class Message(BaseModel):
24 | role: str
25 | content: str
26 |
27 |
28 | class PromptVersion(Protocol):
29 | def __call__(self, context: dict[str, Any]) -> list[Message]: ...
30 |
31 |
32 | PromptFunction = Callable[[dict[str, Any]], list[Message]]
33 |
```
--------------------------------------------------------------------------------
/mcp_server/src/utils/utils.py:
--------------------------------------------------------------------------------
```python
1 | """Utility functions for Graphiti MCP Server."""
2 |
3 | from collections.abc import Callable
4 |
5 |
6 | def create_azure_credential_token_provider() -> Callable[[], str]:
7 | """
8 | Create Azure credential token provider for managed identity authentication.
9 |
10 | Requires azure-identity package. Install with: pip install mcp-server[azure]
11 |
12 | Raises:
13 | ImportError: If azure-identity package is not installed
14 | """
15 | try:
16 | from azure.identity import DefaultAzureCredential, get_bearer_token_provider
17 | except ImportError:
18 | raise ImportError(
19 | 'azure-identity is required for Azure AD authentication. '
20 | 'Install it with: pip install mcp-server[azure]'
21 | ) from None
22 |
23 | credential = DefaultAzureCredential()
24 | token_provider = get_bearer_token_provider(
25 | credential, 'https://cognitiveservices.azure.com/.default'
26 | )
27 | return token_provider
28 |
```
--------------------------------------------------------------------------------
/mcp_server/tests/pytest.ini:
--------------------------------------------------------------------------------
```
1 | [pytest]
2 | # Pytest configuration for Graphiti MCP integration tests
3 |
4 | # Test discovery patterns
5 | python_files = test_*.py
6 | python_classes = Test*
7 | python_functions = test_*
8 |
9 | # Asyncio configuration
10 | asyncio_mode = auto
11 |
12 | # Markers for test categorization
13 | markers =
14 | slow: marks tests as slow (deselect with '-m "not slow"')
15 | integration: marks tests as integration tests requiring external services
16 | unit: marks tests as unit tests
17 | stress: marks tests as stress/load tests
18 | requires_neo4j: test requires Neo4j database
19 | requires_falkordb: test requires FalkorDB
20 | requires_openai: test requires OpenAI API key
21 |
22 | # Test output options
23 | addopts =
24 | -v
25 | --tb=short
26 | --strict-markers
27 | --color=yes
28 | -p no:warnings
29 |
30 | # Timeout for tests (seconds)
31 | timeout = 300
32 |
33 | # Coverage options
34 | testpaths = tests
35 |
36 | # Environment variables for testing
37 | env =
38 | TEST_MODE=true
39 | LOG_LEVEL=INFO
```
--------------------------------------------------------------------------------
/docker-compose.test.yml:
--------------------------------------------------------------------------------
```yaml
1 | services:
2 | graph:
3 | image: graphiti-service:${GITHUB_SHA}
4 | ports:
5 | - "8000:8000"
6 | healthcheck:
7 | test:
8 | [
9 | "CMD",
10 | "python",
11 | "-c",
12 | "import urllib.request; urllib.request.urlopen('http://localhost:8000/healthcheck')",
13 | ]
14 | interval: 10s
15 | timeout: 5s
16 | retries: 3
17 | depends_on:
18 | neo4j:
19 | condition: service_healthy
20 | environment:
21 | - OPENAI_API_KEY=${OPENAI_API_KEY}
22 | - NEO4J_URI=bolt://neo4j:${NEO4J_PORT}
23 | - NEO4J_USER=${NEO4J_USER}
24 | - NEO4J_PASSWORD=${NEO4J_PASSWORD}
25 | - PORT=8000
26 |
27 | neo4j:
28 | image: neo4j:5.26.2
29 | ports:
30 | - "7474:7474"
31 | - "${NEO4J_PORT}:${NEO4J_PORT}"
32 | healthcheck:
33 | test: wget "http://localhost:${NEO4J_PORT}" || exit 1
34 | interval: 1s
35 | timeout: 10s
36 | retries: 20
37 | start_period: 3s
38 | environment:
39 | - NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}
40 |
```
--------------------------------------------------------------------------------
/.github/workflows/typecheck.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Pyright Type Check
2 |
3 | permissions:
4 | contents: read
5 |
6 | on:
7 | push:
8 | branches: ["main"]
9 | pull_request:
10 | branches: ["main"]
11 |
12 | jobs:
13 | pyright:
14 | runs-on: depot-ubuntu-22.04
15 | environment: development
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: Set up Python
19 | id: setup-python
20 | uses: actions/setup-python@v5
21 | with:
22 | python-version: "3.10"
23 | - name: Install uv
24 | uses: astral-sh/setup-uv@v3
25 | with:
26 | version: "latest"
27 | - name: Install dependencies
28 | run: uv sync --all-extras
29 | - name: Run Pyright for graphiti-core
30 | shell: bash
31 | run: |
32 | uv run pyright ./graphiti_core
33 | - name: Install graph-service dependencies
34 | shell: bash
35 | run: |
36 | cd server
37 | uv sync --all-extras
38 | - name: Run Pyright for graph-service
39 | shell: bash
40 | run: |
41 | cd server
42 | uv run pyright .
43 |
```
--------------------------------------------------------------------------------
/graphiti_core/llm_client/utils.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | import logging
18 | from time import time
19 |
20 | from graphiti_core.embedder.client import EmbedderClient
21 |
22 | logger = logging.getLogger(__name__)
23 |
24 |
25 | async def generate_embedding(embedder: EmbedderClient, text: str):
26 | start = time()
27 |
28 | text = text.replace('\n', ' ')
29 | embedding = await embedder.create(input_data=[text])
30 |
31 | end = time()
32 | logger.debug(f'embedded text of length {len(text)} in {end - start} ms')
33 |
34 | return embedding
35 |
```
--------------------------------------------------------------------------------
/server/graph_service/dto/common.py:
--------------------------------------------------------------------------------
```python
1 | from datetime import datetime
2 | from typing import Literal
3 |
4 | from graphiti_core.utils.datetime_utils import utc_now
5 | from pydantic import BaseModel, Field
6 |
7 |
8 | class Result(BaseModel):
9 | message: str
10 | success: bool
11 |
12 |
13 | class Message(BaseModel):
14 | content: str = Field(..., description='The content of the message')
15 | uuid: str | None = Field(default=None, description='The uuid of the message (optional)')
16 | name: str = Field(
17 | default='', description='The name of the episodic node for the message (optional)'
18 | )
19 | role_type: Literal['user', 'assistant', 'system'] = Field(
20 | ..., description='The role type of the message (user, assistant or system)'
21 | )
22 | role: str | None = Field(
23 | description='The custom role of the message to be used alongside role_type (user name, bot name, etc.)',
24 | )
25 | timestamp: datetime = Field(default_factory=utc_now, description='The timestamp of the message')
26 | source_description: str = Field(
27 | default='', description='The description of the source of the message'
28 | )
29 |
```
--------------------------------------------------------------------------------
/graphiti_core/graphiti_types.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from pydantic import BaseModel, ConfigDict
18 |
19 | from graphiti_core.cross_encoder import CrossEncoderClient
20 | from graphiti_core.driver.driver import GraphDriver
21 | from graphiti_core.embedder import EmbedderClient
22 | from graphiti_core.llm_client import LLMClient
23 | from graphiti_core.tracer import Tracer
24 |
25 |
26 | class GraphitiClients(BaseModel):
27 | driver: GraphDriver
28 | llm_client: LLMClient
29 | embedder: EmbedderClient
30 | cross_encoder: CrossEncoderClient
31 | tracer: Tracer
32 |
33 | model_config = ConfigDict(arbitrary_types_allowed=True)
34 |
```
--------------------------------------------------------------------------------
/.github/workflows/release-graphiti-core.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Release to PyPI
2 |
3 | on:
4 | push:
5 | tags: ["v*.*.*"]
6 |
7 | jobs:
8 | release:
9 | runs-on: ubuntu-latest
10 | permissions:
11 | id-token: write
12 | contents: write
13 | environment:
14 | name: release
15 | url: https://pypi.org/p/zep-cloud
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: Set up Python 3.11
19 | uses: actions/setup-python@v5
20 | with:
21 | python-version: "3.11"
22 | - name: Install uv
23 | uses: astral-sh/setup-uv@v3
24 | with:
25 | version: "latest"
26 | - name: Compare pyproject version with tag
27 | run: |
28 | TAG_VERSION=${GITHUB_REF#refs/tags/}
29 | PROJECT_VERSION=$(uv run python -c "import tomllib; print('v' + tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
30 | if [ "$TAG_VERSION" != "$PROJECT_VERSION" ]; then
31 | echo "Tag version $TAG_VERSION does not match the project version $PROJECT_VERSION"
32 | exit 1
33 | fi
34 | - name: Build project for distribution
35 | run: uv build
36 | - name: Publish package distributions to PyPI
37 | uses: pypa/gh-action-pypi-publish@release/v1
38 |
```
--------------------------------------------------------------------------------
/examples/wizard_of_oz/parser.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import re
3 |
4 |
5 | def parse_wizard_of_oz(file_path):
6 | with open(file_path, encoding='utf-8') as file:
7 | content = file.read()
8 |
9 | # Split the content into chapters
10 | chapters = re.split(r'\n\n+Chapter [IVX]+\n', content)[
11 | 1:
12 | ] # Skip the first split which is before Chapter I
13 |
14 | episodes = []
15 | for i, chapter in enumerate(chapters, start=1):
16 | # Extract chapter title
17 | title_match = re.match(r'(.*?)\n\n', chapter)
18 | title = title_match.group(1) if title_match else f'Chapter {i}'
19 |
20 | # Remove the title from the chapter content
21 | chapter_content = chapter[len(title) :].strip() if title_match else chapter.strip()
22 |
23 | # Create episode dictionary
24 | episode = {'episode_number': i, 'title': title, 'content': chapter_content}
25 | episodes.append(episode)
26 |
27 | return episodes
28 |
29 |
30 | def get_wizard_of_oz_messages():
31 | file_path = 'woo.txt'
32 | script_dir = os.path.dirname(__file__)
33 | relative_path = os.path.join(script_dir, file_path)
34 | # Use the function
35 | parsed_episodes = parse_wizard_of_oz(relative_path)
36 | return parsed_episodes
37 |
```
--------------------------------------------------------------------------------
/graphiti_core/embedder/client.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | import os
18 | from abc import ABC, abstractmethod
19 | from collections.abc import Iterable
20 |
21 | from pydantic import BaseModel, Field
22 |
23 | EMBEDDING_DIM = int(os.getenv('EMBEDDING_DIM', 1024))
24 |
25 |
26 | class EmbedderConfig(BaseModel):
27 | embedding_dim: int = Field(default=EMBEDDING_DIM, frozen=True)
28 |
29 |
30 | class EmbedderClient(ABC):
31 | @abstractmethod
32 | async def create(
33 | self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]]
34 | ) -> list[float]:
35 | pass
36 |
37 | async def create_batch(self, input_data_list: list[str]) -> list[list[float]]:
38 | raise NotImplementedError()
39 |
```
--------------------------------------------------------------------------------
/OTEL_TRACING.md:
--------------------------------------------------------------------------------
```markdown
1 | # OpenTelemetry Tracing in Graphiti
2 |
3 | Graphiti supports OpenTelemetry distributed tracing. Tracing is optional - without a tracer, operations use no-op implementations with zero overhead.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | uv add opentelemetry-sdk
9 | ```
10 |
11 | ## Basic Usage
12 |
13 | ```python
14 | from opentelemetry import trace
15 | from opentelemetry.sdk.trace import TracerProvider
16 | from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
17 | from graphiti_core import Graphiti
18 |
19 | # Set up OpenTelemetry
20 | provider = TracerProvider()
21 | provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
22 | trace.set_tracer_provider(provider)
23 |
24 | # Get tracer and pass to Graphiti
25 | tracer = trace.get_tracer(__name__)
26 | graphiti = Graphiti(
27 | uri="bolt://localhost:7687",
28 | user="neo4j",
29 | password="password",
30 | tracer=tracer,
31 | trace_span_prefix="myapp.graphiti" # Optional, defaults to "graphiti"
32 | )
33 | ```
34 |
35 | ## With Kuzu (In-Memory)
36 |
37 | ```python
38 | from graphiti_core.driver.kuzu_driver import KuzuDriver
39 |
40 | kuzu_driver = KuzuDriver()
41 | graphiti = Graphiti(graph_driver=kuzu_driver, tracer=tracer)
42 | ```
43 |
44 | ## Example
45 |
46 | See `examples/opentelemetry/` for a complete working example with stdout tracing
47 |
48 |
```
--------------------------------------------------------------------------------
/tests/evals/utils.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | import logging
18 | import sys
19 |
20 |
21 | def setup_logging():
22 | # Create a logger
23 | logger = logging.getLogger()
24 | logger.setLevel(logging.INFO) # Set the logging level to INFO
25 |
26 | # Create console handler and set level to INFO
27 | console_handler = logging.StreamHandler(sys.stdout)
28 | console_handler.setLevel(logging.INFO)
29 |
30 | # Create formatter
31 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
32 |
33 | # Add formatter to console handler
34 | console_handler.setFormatter(formatter)
35 |
36 | # Add console handler to logger
37 | logger.addHandler(console_handler)
38 |
39 | return logger
40 |
```
--------------------------------------------------------------------------------
/tests/evals/eval_cli.py:
--------------------------------------------------------------------------------
```python
1 | import argparse
2 | import asyncio
3 |
4 | from tests.evals.eval_e2e_graph_building import build_baseline_graph, eval_graph
5 |
6 |
7 | async def main():
8 | parser = argparse.ArgumentParser(
9 | description='Run eval_graph and optionally build_baseline_graph from the command line.'
10 | )
11 |
12 | parser.add_argument(
13 | '--multi-session-count',
14 | type=int,
15 | required=True,
16 | help='Integer representing multi-session count',
17 | )
18 | parser.add_argument('--session-length', type=int, required=True, help='Length of each session')
19 | parser.add_argument(
20 | '--build-baseline', action='store_true', help='If set, also runs build_baseline_graph'
21 | )
22 |
23 | args = parser.parse_args()
24 |
25 | # Optionally run the async function
26 | if args.build_baseline:
27 | print('Running build_baseline_graph...')
28 | await build_baseline_graph(
29 | multi_session_count=args.multi_session_count, session_length=args.session_length
30 | )
31 |
32 | # Always call eval_graph
33 | result = await eval_graph(
34 | multi_session_count=args.multi_session_count, session_length=args.session_length
35 | )
36 | print('Result of eval_graph:', result)
37 |
38 |
39 | if __name__ == '__main__':
40 | asyncio.run(main())
41 |
```
--------------------------------------------------------------------------------
/server/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [project]
2 | name = "graph-service"
3 | version = "0.1.0"
4 | description = "Zep Graph service implementing Graphiti package"
5 | authors = [
6 | { "name" = "Paul Paliychuk", "email" = "[email protected]" },
7 | ]
8 | readme = "README.md"
9 | requires-python = ">=3.10"
10 | dependencies = [
11 | "fastapi>=0.115.0",
12 | "graphiti-core",
13 | "pydantic-settings>=2.4.0",
14 | "uvicorn>=0.30.6",
15 | "httpx>=0.28.1",
16 | ]
17 |
18 | [project.optional-dependencies]
19 | dev = [
20 | "pydantic>=2.8.2",
21 | "pyright>=1.1.380",
22 | "pytest>=8.3.2",
23 | "python-dotenv>=1.0.1",
24 | "pytest-asyncio>=0.24.0",
25 | "pytest-xdist>=3.6.1",
26 | "ruff>=0.6.2",
27 | "fastapi-cli>=0.0.5",
28 | ]
29 |
30 | [build-system]
31 | requires = ["hatchling"]
32 | build-backend = "hatchling.build"
33 |
34 | [tool.hatch.build.targets.wheel]
35 | packages = ["graph_service"]
36 |
37 | [tool.pytest.ini_options]
38 | pythonpath = ["."]
39 |
40 | [tool.ruff]
41 | line-length = 100
42 |
43 | [tool.ruff.lint]
44 | select = [
45 | # pycodestyle
46 | "E",
47 | # Pyflakes
48 | "F",
49 | # pyupgrade
50 | "UP",
51 | # flake8-bugbear
52 | "B",
53 | # flake8-simplify
54 | "SIM",
55 | # isort
56 | "I",
57 | ]
58 | ignore = ["E501"]
59 |
60 | [tool.ruff.format]
61 | quote-style = "single"
62 | indent-style = "space"
63 | docstring-code-format = true
64 |
65 | [tool.pyright]
66 | include = ["."]
67 | pythonVersion = "3.10"
68 | typeCheckingMode = "standard"
69 |
```
--------------------------------------------------------------------------------
/graphiti_core/llm_client/errors.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 |
18 | class RateLimitError(Exception):
19 | """Exception raised when the rate limit is exceeded."""
20 |
21 | def __init__(self, message='Rate limit exceeded. Please try again later.'):
22 | self.message = message
23 | super().__init__(self.message)
24 |
25 |
26 | class RefusalError(Exception):
27 | """Exception raised when the LLM refuses to generate a response."""
28 |
29 | def __init__(self, message: str):
30 | self.message = message
31 | super().__init__(self.message)
32 |
33 |
34 | class EmptyResponseError(Exception):
35 | """Exception raised when the LLM returns an empty response."""
36 |
37 | def __init__(self, message: str):
38 | self.message = message
39 | super().__init__(self.message)
40 |
```
--------------------------------------------------------------------------------
/graphiti_core/utils/ontology_utils/entity_types_utils.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | from pydantic import BaseModel
18 |
19 | from graphiti_core.errors import EntityTypeValidationError
20 | from graphiti_core.nodes import EntityNode
21 |
22 |
23 | def validate_entity_types(
24 | entity_types: dict[str, type[BaseModel]] | None,
25 | ) -> bool:
26 | if entity_types is None:
27 | return True
28 |
29 | entity_node_field_names = EntityNode.model_fields.keys()
30 |
31 | for entity_type_name, entity_type_model in entity_types.items():
32 | entity_type_field_names = entity_type_model.model_fields.keys()
33 | for entity_type_field_name in entity_type_field_names:
34 | if entity_type_field_name in entity_node_field_names:
35 | raise EntityTypeValidationError(entity_type_name, entity_type_field_name)
36 |
37 | return True
38 |
```
--------------------------------------------------------------------------------
/server/graph_service/dto/retrieve.py:
--------------------------------------------------------------------------------
```python
1 | from datetime import datetime, timezone
2 |
3 | from pydantic import BaseModel, Field
4 |
5 | from graph_service.dto.common import Message
6 |
7 |
8 | class SearchQuery(BaseModel):
9 | group_ids: list[str] | None = Field(
10 | None, description='The group ids for the memories to search'
11 | )
12 | query: str
13 | max_facts: int = Field(default=10, description='The maximum number of facts to retrieve')
14 |
15 |
16 | class FactResult(BaseModel):
17 | uuid: str
18 | name: str
19 | fact: str
20 | valid_at: datetime | None
21 | invalid_at: datetime | None
22 | created_at: datetime
23 | expired_at: datetime | None
24 |
25 | class Config:
26 | json_encoders = {datetime: lambda v: v.astimezone(timezone.utc).isoformat()}
27 |
28 |
29 | class SearchResults(BaseModel):
30 | facts: list[FactResult]
31 |
32 |
33 | class GetMemoryRequest(BaseModel):
34 | group_id: str = Field(..., description='The group id of the memory to get')
35 | max_facts: int = Field(default=10, description='The maximum number of facts to retrieve')
36 | center_node_uuid: str | None = Field(
37 | ..., description='The uuid of the node to center the retrieval on'
38 | )
39 | messages: list[Message] = Field(
40 | ..., description='The messages to build the retrieval query from '
41 | )
42 |
43 |
44 | class GetMemoryResponse(BaseModel):
45 | facts: list[FactResult] = Field(..., description='The facts that were retrieved from the graph')
46 |
```
--------------------------------------------------------------------------------
/mcp_server/docker/docker-compose.yml:
--------------------------------------------------------------------------------
```yaml
1 | services:
2 | graphiti-falkordb:
3 | image: zepai/knowledge-graph-mcp:latest
4 | build:
5 | context: ..
6 | dockerfile: docker/Dockerfile
7 | args:
8 | GRAPHITI_CORE_VERSION: ${GRAPHITI_CORE_VERSION:-0.23.0}
9 | MCP_SERVER_VERSION: ${MCP_SERVER_VERSION:-1.0.0}
10 | BUILD_DATE: ${BUILD_DATE:-}
11 | VCS_REF: ${VCS_REF:-}
12 | env_file:
13 | - path: ../.env
14 | required: false
15 | environment:
16 | # FalkorDB configuration
17 | - FALKORDB_PASSWORD=${FALKORDB_PASSWORD:-}
18 | - BROWSER=${BROWSER:-1} # Enable FalkorDB Browser UI (set to 0 to disable)
19 | # MCP Server configuration
20 | - FALKORDB_URI=redis://localhost:6379
21 | - FALKORDB_DATABASE=${FALKORDB_DATABASE:-default_db}
22 | - GRAPHITI_GROUP_ID=${GRAPHITI_GROUP_ID:-main}
23 | - SEMAPHORE_LIMIT=${SEMAPHORE_LIMIT:-10}
24 | - CONFIG_PATH=/app/mcp/config/config.yaml
25 | - PATH=/root/.local/bin:${PATH}
26 | volumes:
27 | - falkordb_data:/var/lib/falkordb/data
28 | - mcp_logs:/var/log/graphiti
29 | ports:
30 | - "6379:6379" # FalkorDB/Redis
31 | - "3000:3000" # FalkorDB web UI
32 | - "8000:8000" # MCP server HTTP
33 | healthcheck:
34 | test: ["CMD", "redis-cli", "-p", "6379", "ping"]
35 | interval: 10s
36 | timeout: 5s
37 | retries: 5
38 | start_period: 15s
39 |
40 | volumes:
41 | falkordb_data:
42 | driver: local
43 | mcp_logs:
44 | driver: local
45 |
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve Graphiti
4 | title: '[BUG] '
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | ## Bug Description
10 | A clear and concise description of what the bug is.
11 |
12 | ## Steps to Reproduce
13 | Provide a minimal code example that reproduces the issue:
14 |
15 | ```python
16 | # Your code here
17 | ```
18 |
19 | ## Expected Behavior
20 | A clear and concise description of what you expected to happen.
21 |
22 | ## Actual Behavior
23 | A clear and concise description of what actually happened.
24 |
25 | ## Environment
26 | - **Graphiti Version**: [e.g. 0.15.1]
27 | - **Python Version**: [e.g. 3.11.5]
28 | - **Operating System**: [e.g. macOS 14.0, Ubuntu 22.04]
29 | - **Database Backend**: [e.g. Neo4j 5.26, FalkorDB 1.1.2]
30 | - **LLM Provider & Model**: [e.g. OpenAI gpt-4.1, Anthropic claude-4-sonnet, Google gemini-2.5-flash]
31 |
32 | ## Installation Method
33 | - [ ] pip install
34 | - [ ] uv add
35 | - [ ] Development installation (git clone)
36 |
37 | ## Error Messages/Traceback
38 | ```
39 | Paste the full error message and traceback here
40 | ```
41 |
42 | ## Configuration
43 | ```python
44 | # Relevant configuration or initialization code
45 | ```
46 |
47 | ## Additional Context
48 | - Does this happen consistently or intermittently?
49 | - Which component are you using? (core library, REST server, MCP server)
50 | - Any recent changes to your environment?
51 | - Related issues or similar problems you've encountered?
52 |
53 | ## Possible Solution
54 | If you have ideas about what might be causing the issue or how to fix it, please share them here.
```
--------------------------------------------------------------------------------
/graphiti_core/prompts/prompt_helpers.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Copyright 2024, Zep Software, Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | """
16 |
17 | import json
18 | from typing import Any
19 |
20 | DO_NOT_ESCAPE_UNICODE = '\nDo not escape unicode characters.\n'
21 |
22 |
23 | def to_prompt_json(data: Any, ensure_ascii: bool = False, indent: int | None = None) -> str:
24 | """
25 | Serialize data to JSON for use in prompts.
26 |
27 | Args:
28 | data: The data to serialize
29 | ensure_ascii: If True, escape non-ASCII characters. If False (default), preserve them.
30 | indent: Number of spaces for indentation. Defaults to None (minified).
31 |
32 | Returns:
33 | JSON string representation of the data
34 |
35 | Notes:
36 | By default (ensure_ascii=False), non-ASCII characters (e.g., Korean, Japanese, Chinese)
37 | are preserved in their original form in the prompt, making them readable
38 | in LLM logs and improving model understanding.
39 | """
40 | return json.dumps(data, ensure_ascii=ensure_ascii, indent=indent)
41 |
```