# Technical Documentation: getzep/graphiti

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/getzep/graphiti)

### Overview (`README.md`)

<p align="center">
  <a href="https://www.getzep.com/">
    <img src="https://github.com/user-attachments/assets/119c5682-9654-4257-8922-56b7cb8ffd73" width="150" alt="Zep Logo">
  </a>
</p>

<h1 align="center">
Graphiti
</h1>
<h2 align="center">A Framework for Building Temporal Knowledge Graphs</h2>

<div align="center">

[](https://github.com/getzep/Graphiti/actions/workflows/lint.yml)
[](https://github.com/getzep/Graphiti/actions/workflows/unit_tests.yml)
[](https://github.com/getzep/Graphiti/actions/workflows/typecheck.yml)

[](https://github.com/getzep/graphiti/stargazers)
[](https://arxiv.org/abs/2501.13956)
[](https://github.com/getzep/graphiti/releases)

</div>
<div align="center">

<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>

</div>

> [!NOTE]
> **We're Hiring!** Build context graphs that power reliable, personalized, fast production AI agents.
> Come build with us — we're hiring Engineers and Developer Relations folks. [View open roles](https://www.getzep.com/careers/).

⭐ *Help us reach more developers and grow the Graphiti community. Star this repo!*

&nbsp;

Graphiti is a framework for building and querying temporal context graphs for AI agents. Unlike static knowledge graphs,
Graphiti's context graphs track how facts change over time, maintain provenance to source data, and support both
prescribed and learned ontology — making them purpose-built for agents operating on evolving, real-world data.

Unlike traditional retrieval-augmented generation (RAG) methods, Graphiti continuously integrates user interactions,
structured and unstructured enterprise data, and external information into a coherent, queryable graph. The framework
supports incremental data updates, efficient retrieval, and precise historical queries without requiring complete graph
recomputation, making it suitable for developing interactive, context-aware AI applications.

Use Graphiti to:

- Build temporal knowledge graphs that evolve with every interaction — tracking what's true now and what was true before.
- Represent rich, structured context instead of flat document chunks or raw event streams.
- Query across time, meaning, and relationships with hybrid retrieval (semantic + keyword + graph traversal).

&nbsp;

<p align="center">
    <img src="images/graphiti-graph-intro.gif" alt="Graphiti temporal walkthrough" width="700px">
</p>

&nbsp;

## What is a Context Graph?

A **context graph** is a temporal graph of entities, relationships, and facts — like *"Kendra loves Adidas shoes (as of
March 2026)."* Unlike traditional knowledge graphs, each fact in a context graph has a validity window: when it became
true, and when (if ever) it was superseded. Entities evolve over time with updated summaries. Everything traces back to
**episodes** — the raw data that produced it.

What makes Graphiti unique is its ability to autonomously build context graphs from unstructured and structured data,
handling changing relationships while preserving full temporal history.

A context graph contains:

| Component | What it stores |
|-----------|---------------|
| **Entities** (nodes) | People, products, policies, concepts — with summaries that evolve over time |
| **Facts / Relationships** (edges) | Triplets (Entity → Relationship → Entity) with temporal validity windows |
| **Episodes** (provenance) | Raw data as ingested — the ground truth stream. Every derived fact traces back here |
| **Custom Types** (ontology) | Developer-defined entity and edge types via Pydantic models |

## Graphiti and Zep

Graphiti is an open-source Python framework for building and querying temporal context graphs. You supply the graph
database, LLM, and embedder, and you build the service around it: ingestion, tenancy, access control, and operations.

[Zep](https://www.getzep.com) is the unified context layer for enterprises. Zep brings data from across a business into
governed context graphs and serves that context to agents in production. Graphiti is the graph construction framework
inside Zep. [Konig](https://www.getzep.com/platform/context-graph-engine/), Zep's graph database service, stores and
serves millions of context graphs with low-latency retrieval.

Teams use both to give agents memory. Neither is an agent memory product: Graphiti is a framework for building graphs,
and Zep unifies, governs, and serves an enterprise's context.

Read our paper: [Zep: A Temporal Knowledge Graph Architecture](https://arxiv.org/abs/2501.13956).

<p align="center">
    <a href="https://arxiv.org/abs/2501.13956"><img src="images/arxiv-screenshot.png" alt="Zep: A Temporal Knowledge Graph Architecture" width="700px"></a>
</p>

## Zep vs Graphiti

| Aspect | Zep | Graphiti |
|--------|-----|---------|
| **What it is** | The unified context layer for enterprises | An open-source framework for building temporal context graphs |
| **Scope** | Unifies data from across an enterprise into governed context and serves it to agents | Builds and queries graphs inside your own application |
| **Context graphs** | Manages millions of context graphs with governance | Builds and queries the graphs you define |
| **Graph database** | [Konig](https://www.getzep.com/platform/context-graph-engine/), Zep's graph database service, built for millions of context graphs with low-latency retrieval; no third-party graph database required | Bring your own: Neo4j, FalkorDB, or Amazon Neptune |
| **Users, threads, and ingestion** | Built-in users, threads, and ingestion for messages and business data | Build your own |
| **Retrieval & performance** | Pre-configured, production-ready retrieval with sub-200ms performance at scale | Custom implementation required; performance depends on your setup |
| **Developer tools** | Dashboard with graph visualization, debug logs, API logs; CLI; SDKs for Python, TypeScript, and Go | Build your own tools |
| **Enterprise features** | SLAs, support, security guarantees | Self-managed |
| **Deployment** | Fully managed or in your cloud (BYOC) | Self-hosted only |

### When to choose which

**Choose Zep** if you want an enterprise's context unified, governed, and served to agents in production, without
building and operating that system yourself.

**Choose Graphiti** if you are building temporal context graphs into your own system and will operate the graph
database, retrieval, and surrounding service yourself.

## Why Graphiti?

Traditional RAG approaches often rely on batch processing and static data summarization, making them inefficient for
frequently changing data. Graphiti addresses these challenges by providing:

- **Temporal Fact Management:** Facts have validity windows. When information changes, old facts are
  invalidated — not deleted. Query what's true now, or what was true at any point in time.
- **Episodes & Provenance:** Every entity and relationship traces back to the episodes (raw data) that produced it.
  Full lineage from derived fact to source.
- **Prescribed & Learned Ontology:** Define entity and edge types upfront via Pydantic models (prescribed), or let
  structure emerge from your data (learned). Start simple, evolve as patterns appear.
- **Incremental Graph Construction:** New data integrates immediately without batch recomputation. The graph evolves
  in real-time as episodes are ingested.
- **Hybrid Retrieval:** Combines semantic embeddings, keyword (BM25), and graph traversal for low-latency,
  high-precision queries without reliance on LLM summarization.
- **Scalability:** Efficiently manages large datasets with parallel processing, pluggable graph backends, suitable
  for enterprise workloads.

<p align="center">
    <img src="/images/graphiti-intro-slides-stock-2.gif" alt="Graphiti structured + unstructured demo" width="700px">
</p>

## Graphiti vs. GraphRAG

| Aspect | GraphRAG | Graphiti |
|--------|----------|---------|
| **Primary Use** | Static document summarization | Dynamic, evolving temporal knowledge graphs |
| **Data Handling** | Batch-oriented processing | Continuous, incremental updates |
| **Knowledge Structure** | Entity clusters & community summaries | Temporal context graph — entities, facts with validity windows, episodes, communities |
| **Retrieval Method** | Sequential LLM summarization | Hybrid semantic, keyword, and graph-based search |
| **Adaptability** | Low | High |
| **Temporal Handling** | Basic timestamp tracking | Explicit bi-temporal tracking with automatic fact invalidation |
| **Contradiction Handling** | LLM-driven summarization judgments | Automatic fact invalidation with temporal history preserved |
| **Query Latency** | Seconds to tens of seconds | Typically sub-second latency |
| **Custom Entity Types** | No | Yes, customizable via Pydantic models |
| **Scalability** | Moderate | High, optimized for large datasets |

Graphiti is specifically designed to address the challenges of dynamic and frequently updated datasets, making it
particularly suitable for applications requiring real-time interaction and precise historical queries.

## Installation

Requirements:

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

> [!IMPORTANT]
> Graphiti works best with LLM services that support Structured Output (such as OpenAI, Anthropic, and Gemini).
> Using other services may result in incorrect output schemas and ingestion failures. This is particularly
> problematic when using smaller models.

Optional:

- Google Gemini, Anthropic, or Groq API key (for alternative LLM providers)

> [!TIP]
> The simplest way to install Neo4j is via [Neo4j Desktop](https://neo4j.com/download/). It provides a user-friendly
> interface to manage Neo4j instances and databases.
> Alternatively, you can use FalkorDB on-premises via Docker and instantly start with the quickstart example:
> ```
> docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest
> ```

```bash
pip install graphiti-core
```

or

```bash
uv add graphiti-core
```

### Installing with FalkorDB Support

If you plan to use FalkorDB as your graph database backend, install with the FalkorDB extra:

```bash
pip install graphiti-core[falkordb]

# or with uv
uv add graphiti-core[falkordb]

# or embedded version (requires Python 3.12+)
pip install graphiti-core[falkordblite]
# or with uv
uv add graphiti-core[falkordblite]
```

### Installing with Kuzu Support

> [!WARNING]
> **Kuzu is deprecated** and will be removed in a future release — the upstream Kuzu project is no longer
> maintained. New projects should use Neo4j or FalkorDB. The driver still ships for now but emits a
> `DeprecationWarning`.

If you plan to use Kuzu as your graph database backend, install with the Kuzu extra:

```bash
pip install graphiti-core[kuzu]

# or with uv
uv add graphiti-core[kuzu]
```

### Installing with Amazon Neptune Support

If you plan to use Amazon Neptune as your graph database backend, install with the Amazon Neptune extra:

```bash
pip install graphiti-core[neptune]

# or with uv
uv add graphiti-core[neptune]
```

### You can also install optional LLM providers as extras:

```bash
# Install with Anthropic support
pip install graphiti-core[anthropic]

# Install with Groq support
pip install graphiti-core[groq]

# Install with Google Gemini support
pip install graphiti-core[google-genai]

# Install with multiple providers
pip install graphiti-core[anthropic,groq,google-genai]

# Install with FalkorDB and LLM providers
pip install graphiti-core[falkordb,anthropic,google-genai]

# Install with Amazon Neptune
pip install graphiti-core[neptune]
```

## Default to Low Concurrency; LLM Provider 429 Rate Limit Errors

Graphiti's ingestion pipelines are designed for high concurrency. By default, concurrency is set low to avoid LLM
Provider 429 Rate Limit Errors. If you find Graphiti slow, please increase concurrency as described below.

Concurrency controlled by the `SEMAPHORE_LIMIT` environment variable. By default, `SEMAPHORE_LIMIT` is set to `10`
concurrent operations to help prevent `429` rate limit errors from your LLM provider. If you encounter such errors, try
lowering this value.

If your LLM provider allows higher throughput, you can increase `SEMAPHORE_LIMIT` to boost episode ingestion
performance.

## Quick Start

> [!IMPORTANT]
> Graphiti defaults to using OpenAI for LLM inference and embedding. Ensure that an `OPENAI_API_KEY` is set in your
> environment.
> Support for Anthropic, Gemini, and Groq is available, too. Other LLM providers — both hosted OpenAI-compatible APIs
> (DeepSeek, Together, OpenRouter, …) and local servers (Ollama, vLLM, llama.cpp, LM Studio) — may be used via their
> OpenAI-compatible endpoints; see
> [Using Graphiti with OpenAI-compatible providers and local LLMs](#using-graphiti-with-openai-compatible-providers-and-local-llms).

For a complete working example, see the [Quickstart Example](examples/quickstart/README.md) in the examples directory.
The quickstart demonstrates:

1. Connecting to a Neo4j, Amazon Neptune, FalkorDB, or Kuzu database
2. Initializing Graphiti indices and constraints
3. Adding episodes to the graph (both text and structured JSON)
4. Searching for relationships (edges) using hybrid search
5. Reranking search results using graph distance
6. Searching for nodes using predefined search recipes

The example is fully documented with clear explanations of each functionality and includes a comprehensive README with
setup instructions and next steps.

### Running with Docker Compose

You can use Docker Compose to quickly start the required services:

- **Neo4j Docker:**

  ```bash
  docker compose up
  ```

  This will start the Neo4j Docker service and related components.

- **FalkorDB Docker:**

  ```bash
  docker compose --profile falkordb up
  ```

  This will start the FalkorDB Docker service and related components.

## Reference MCP and REST servers

The `mcp_server` and `server` directories contain reference implementations of an MCP server and a REST server. These
servers show how to put Graphiti behind a protocol. These servers are not production deployments.

> [!WARNING]
> The reference servers have no authentication, no authorization, and no prompt-injection protection. These omissions
> are intentional. Do not expose the reference servers to an untrusted network.

The reference servers have these limits:

- The servers do not authenticate callers. Any caller can read, change, or delete data in any `group_id`.
- The servers do not detect or block prompt injection. The servers send episode content and tool input to the
  configured LLM without a filter.
- The example Docker Compose files use default database credentials.

Security reports about these limits are out of scope. Pull requests that add authentication, authorization, or
prompt-injection filters to the reference servers are also out of scope. For more information, see the
[security policy scope](SECURITY.md#scope).

For a production deployment, use one of these options:

- Use the `graphiti-core` library in your own service. Your service must supply authentication, authorization, tenant
  isolation, and input controls.
- Use [Zep](https://www.getzep.com), the unified context layer for enterprises, which is built on Graphiti. Zep is
  available as a managed service or in your own cloud (BYOC). For a comparison, see [Zep vs Graphiti](#zep-vs-graphiti).

### MCP Server

The `mcp_server` directory contains a reference Model Context Protocol (MCP) server for Graphiti. This server
allows AI assistants to interact with Graphiti's context graph capabilities through the MCP protocol.

Key features of the MCP server include:

- Episode management (add, retrieve, delete)
- Entity management and relationship handling
- Semantic and hybrid search capabilities
- Group management for organizing related data
- Graph maintenance operations

The MCP server can be deployed using Docker with Neo4j, making it easy to integrate Graphiti into your AI assistant
workflows.

For detailed setup instructions and usage examples, see the [MCP server README](mcp_server/README.md).

### REST Service

The `server` directory contains a reference REST service for the Graphiti API. The service uses FastAPI.

Please see the [server README](server/README.md) for more information.

## Optional Environment Variables

In addition to the Neo4j and OpenAi-compatible credentials, Graphiti also has a few optional environment variables.
If you are using one of our supported models, such as Anthropic or Voyage models, the necessary environment variables
must be set.

### Database Configuration

Database names are configured directly in the driver constructors:

- **Neo4j**: Database name defaults to `neo4j` (hardcoded in Neo4jDriver)
- **FalkorDB**: Database name defaults to `default_db` (hardcoded in FalkorDriver)

As of v0.17.0, if you need to customize your database configuration, you can instantiate a database driver and pass it
to the Graphiti constructor using the `graph_driver` parameter.

#### Neo4j with Custom Database Name

```python
from graphiti_core import Graphiti
from graphiti_core.driver.neo4j_driver import Neo4jDriver

# Create a Neo4j driver with custom database name
driver = Neo4jDriver(
    uri="bolt://localhost:7687",
    user="neo4j",
    password="password",
    database="my_custom_database"  # Custom database name
)

# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
```

#### FalkorDB with Custom Database Name

```python
from graphiti_core import Graphiti
from graphiti_core.driver.falkordb_driver import FalkorDriver

# Create a FalkorDB driver with custom database name
driver = FalkorDriver(
    host="localhost",
    port=6379,
    username="falkor_user",  # Optional
    password="falkor_password",  # Optional
    database="my_custom_graph"  # Custom database name
)

# Or use embedded FalkorDB Lite (requires Python 3.12+)
# from redislite.async_falkordb_client import AsyncFalkorDB
# falkordb_client = AsyncFalkorDB(dbfilename='/path/to/database.db')
# driver = FalkorDriver(falkor_db=falkordb_client)

# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
```

#### Kuzu

> [!WARNING]
> Kuzu is **deprecated** (upstream project unmaintained) and will be removed in a future release. Prefer Neo4j or
> FalkorDB.

```python
from graphiti_core import Graphiti
from graphiti_core.driver.kuzu_driver import KuzuDriver

# Create a Kuzu driver
driver = KuzuDriver(db="/tmp/graphiti.kuzu")

# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
```

#### Amazon Neptune

```python
from graphiti_core import Graphiti
from graphiti_core.driver.neptune_driver import NeptuneDriver

# Create a Neptune driver
driver = NeptuneDriver(
    host='<NEPTUNE_ENDPOINT>',
    aoss_host='<AMAZON_OPENSEARCH_SERVERLESS_HOST>',
    port=8182,      # Optional, defaults to 8182
    aoss_port=443,  # Optional, defaults to 443
)

# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
```

Contributing a new graph backend? See [Adding a graph driver](CONTRIBUTING.md#adding-a-graph-driver).

## Using Graphiti with Azure OpenAI

Graphiti supports Azure OpenAI for both LLM inference and embeddings using Azure's OpenAI v1 API compatibility layer.

### Quick Start

```python
from openai import AsyncOpenAI
from graphiti_core import Graphiti
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient

# Initialize Azure OpenAI client using the standard OpenAI client
# with Azure's v1 API endpoint
azure_client = AsyncOpenAI(
    base_url="https://your-resource-name.openai.azure.com/openai/v1/",
    api_key="your-api-key",
)

# Create LLM and Embedder clients
llm_client = AzureOpenAILLMClient(
    azure_client=azure_client,
    config=LLMConfig(model="gpt-5-mini", small_model="gpt-5-mini")  # Your Azure deployment name
)
embedder_client = AzureOpenAIEmbedderClient(
    azure_client=azure_client,
    model="text-embedding-3-small"  # Your Azure embedding deployment name
)

# Initialize Graphiti with Azure OpenAI clients
graphiti = Graphiti(
    "bolt://localhost:7687",
    "neo4j",
    "password",
    llm_client=llm_client,
    embedder=embedder_client,
)

# Now you can use Graphiti with Azure OpenAI
```

**Key Points:**

- Use the standard `AsyncOpenAI` client with Azure's v1 API endpoint format:
  `https://your-resource-name.openai.azure.com/openai/v1/`
- The deployment names (e.g., `gpt-5-mini`, `text-embedding-3-small`) should match your Azure OpenAI deployment names
- See `examples/azure-openai/` for a complete working example

Make sure to replace the placeholder values with your actual Azure OpenAI credentials and deployment names.

## Using Graphiti with Google Gemini

Graphiti supports Google's Gemini models for LLM inference, embeddings, and cross-encoding/reranking. To use Gemini,
you'll need to configure the LLM client, embedder, and the cross-encoder with your Google API key.

Install Graphiti:

```bash
uv add "graphiti-core[google-genai]"

# or

pip install "graphiti-core[google-genai]"
```

```python
from graphiti_core import Graphiti
from graphiti_core.llm_client.gemini_client import GeminiClient, LLMConfig
from graphiti_core.embedder.gemini import GeminiEmbedder, GeminiEmbedderConfig
from graphiti_core.cross_encoder.gemini_reranker_client import GeminiRerankerClient

# Google API key configuration
api_key = "<your-google-api-key>"

# Initialize Graphiti with Gemini clients
graphiti = Graphiti(
    "bolt://localhost:7687",
    "neo4j",
    "password",
    llm_client=GeminiClient(
        config=LLMConfig(
            api_key=api_key,
            model="gemini-2.0-flash"
        )
    ),
    embedder=GeminiEmbedder(
        config=GeminiEmbedderConfig(
            api_key=api_key,
            embedding_model="embedding-001"
        )
    ),
    cross_encoder=GeminiRerankerClient(
        config=LLMConfig(
            api_key=api_key,
            model="gemini-2.5-flash-lite"
        )
    )
)

# Now you can use Graphiti with Google Gemini for all components
```

The Gemini reranker uses the `gemini-2.5-flash-lite` model by default, which is optimized for
cost-effective and low-latency classification tasks. It uses the same boolean classification approach as the OpenAI
reranker, leveraging Gemini's log probabilities feature to rank passage relevance.

## Using Graphiti with OpenAI-compatible providers and local LLMs

Graphiti can use any OpenAI-compatible `/v1` endpoint for LLM inference via `OpenAIGenericClient` — both **hosted
providers** (DeepSeek, Together, OpenRouter, Fireworks, etc.) and **local servers** (Ollama, vLLM, llama.cpp, LM
Studio). Local servers are ideal for privacy-focused applications or avoiding API costs. The example below uses Ollama;
for any other provider, point `base_url` at its endpoint and set the appropriate `api_key` and `model`.

**Note:** Use `OpenAIGenericClient` (not `OpenAIClient`) for these endpoints. It is optimized for local models with a
higher default max token limit (16K vs 8K) and handles structured outputs across compatible providers.

Install the models:

```bash
ollama pull deepseek-r1:7b # LLM
ollama pull nomic-embed-text # embeddings
```

```python
from graphiti_core import Graphiti
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient

# Configure Ollama LLM client
llm_config = LLMConfig(
    api_key="ollama",  # Ollama doesn't require a real API key, but some placeholder is needed
    model="deepseek-r1:7b",
    small_model="deepseek-r1:7b",
    base_url="http://localhost:11434/v1",  # Ollama's OpenAI-compatible endpoint
)

llm_client = OpenAIGenericClient(config=llm_config)

# Initialize Graphiti with Ollama clients
graphiti = Graphiti(
    "bolt://localhost:7687",
    "neo4j",
    "password",
    llm_client=llm_client,
    embedder=OpenAIEmbedder(
        config=OpenAIEmbedderConfig(
            api_key="ollama",  # Placeholder API key
            embedding_model="nomic-embed-text",
            embedding_dim=768,
            base_url="http://localhost:11434/v1",
        )
    ),
    cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config),
)

# Now you can use Graphiti with local Ollama models
```

Ensure Ollama is running (`ollama serve`) and that you have pulled the models you want to use.

### Structured output and small models

Graphiti depends on structured (JSON) output for entity/edge extraction and deduplication, and works best with models
and providers that reliably honor it (OpenAI, Anthropic, Gemini). Reliability varies across OpenAI-compatible providers and
especially on smaller or local models, so `OpenAIGenericClient` exposes a `structured_output_mode`:

- `"json_schema"` (default): requests native structured output via `response_format`. Best on capable models and
  providers that enforce the schema via constrained decoding.
- `"json_object"`: requests plain-JSON mode and injects the schema into the prompt instead. Use this for
  providers/models that don't reliably honor `json_schema` — including some local servers that accept the `json_schema`
  request but don't actually constrain output to it, where `json_object` can be *more* reliable.

When using smaller or local models:

- Prefer the most capable model you can run. Very small models frequently emit JSON that doesn't match the requested
  schema, which surfaces as extraction failures.
- Responses wrapped in Markdown ` ```json ` code fences are stripped automatically.
- Keep `SEMAPHORE_LIMIT` low (see [above](#default-to-low-concurrency-llm-provider-429-rate-limit-errors)) — local
  servers and some providers have limited concurrency.

## Configurable Prompts

Graphiti lets you override LLM prompt builders per client instance. Override callables must return
``ChatPrompt`` (system + user). Response schemas are fixed and not overridable:

```python
from graphiti_core import Graphiti
from graphiti_core.prompts import ChatPrompt, SystemMessage, UserMessage, create_prompt_library

def custom_extract_message(context: dict) -> ChatPrompt:
    return ChatPrompt(
        system=SystemMessage(content='Extract entities for my domain.'),
        user=UserMessage(content=str(context.get('episode_content', ''))),
    )

prompt_library = create_prompt_library({
    'extract_nodes': {
        'extract_message': custom_extract_message,
    },
})

graphiti = Graphiti(..., prompt_library=prompt_library)
```

Unspecified prompts keep the built-in defaults. Returning ``list[Message]`` from an override raises
``TypeError`` — migrate with ``return ChatPrompt(system=..., user=...)``. Unicode handling is applied
via ``ChatPrompt.as_messages()``.

The package-level ``prompt_library`` keeps the existing ``list[Message]`` API.
The opt-in prompt customization API uses ``ChatPromptLibrary`` and
``DefaultChatPromptLibrary``.

For per-prompt routing across providers, pass an opt-in ``llm_runtime``. Create
one ``LLMTransport`` for each provider client. Each ``LLMModel`` binds a provider
model ID to one transport. An unrouted prompt uses the default model.
``llm_runtime`` cannot be combined with ``llm_client`` or ``prompt_library``.
Response schemas stay fixed. You can configure prompt text and model routes:

```python
from typing import Literal

from graphiti_core import Graphiti
from graphiti_core.llm_client.anthropic_client import AnthropicClient
from graphiti_core.llm_client import (
    LLMRuntime,
    LLMTransport,
    OpenAIClient,
    PromptRoutes,
)

OpenAIModels = Literal['gpt-5.1', 'gpt-5-nano']
openai = LLMTransport[OpenAIModels](
    OpenAIClient(),
    models=['gpt-5.1', 'gpt-5-nano'],
)

AnthropicModels = Literal['claude-sonnet-4-5', 'claude-haiku-4-5']
anthropic = LLMTransport[AnthropicModels](
    AnthropicClient(),
    models=['claude-sonnet-4-5', 'claude-haiku-4-5'],
)

main = openai.model('gpt-5.1')
nano = openai.model('gpt-5-nano')
haiku = anthropic.model('claude-haiku-4-5')
runtime = LLMRuntime(
    model=main,
    routes=PromptRoutes(
        extract_nodes=PromptRoutes.ExtractNodes(extract_attributes=nano),
        dedupe_edges=PromptRoutes.DedupeEdges(
            resolve_edge=haiku,
        ),
    ),
)

graphiti = Graphiti(..., llm_runtime=runtime)
```

``LLMModel`` does not have a ``small_id`` field. The runtime passes
``model_size`` for legacy compatibility, but this value does not select a
smaller model. The selected model ID controls provider calls. A routed prompt
uses that model ID. An unrouted prompt uses the default model ID. Route prompts
that need a smaller model to that model explicitly. Legacy calls without
``llm_runtime`` retain ``LLMConfig.small_model`` behavior.

GLiNER2 binds model objects at initialization. It does not support per-prompt
model routing. See ``spec/llm-runtime.md`` for the runtime API.

### Jev dedupe

JevClient routes node dedupe and edge resolution to TypeSafe Jev. Set
``JEV_API_KEY`` before you create the client. The duplicate threshold defaults
to 0.7. The contradiction threshold defaults to 0.8.

Jev is a decision model. It answers typed questions with probabilities. It
does not generate text. Use a general LLM as the default model. Route only
``dedupe_nodes.nodes`` and ``dedupe_edges.resolve_edge`` to Jev. Do not use Jev
for extraction, summaries, or other generation tasks.

TypeSafe hosts the Jev API. Graphiti sends dedupe prompt content to TypeSafe.
See the [FalkorDB Jev dedupe example](examples/jev/) for a runnable example.

```python
from graphiti_core import Graphiti
from graphiti_core.llm_client import (
    JevClient,
    LLMRuntime,
    LLMTransport,
    OpenAIClient,
    PromptRoutes,
    jev_prompt_overrides,
)

openai = LLMTransport(OpenAIClient(), models=['gpt-6-luna'])
jev = LLMTransport(JevClient(), models=['jev-latest'])
jev_dedupe = jev.model('jev-latest', prompt_overrides=jev_prompt_overrides())

runtime = LLMRuntime(
    model=openai.model('gpt-6-luna'),
    routes=PromptRoutes(
        dedupe_nodes=PromptRoutes.DedupeNodes(nodes=jev_dedupe),
        dedupe_edges=PromptRoutes.DedupeEdges(resolve_edge=jev_dedupe),
    ),
)
graphiti = Graphiti(..., llm_runtime=runtime)
```
/* Detailed source-code truncated for AI context efficiency. */
```bash
export GRAPHITI_TELEMETRY_ENABLED=false
```

**Option 2: Set in your shell profile**

```bash
# For bash users (~/.bashrc or ~/.bash_profile)
echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.bashrc

# For zsh users (~/.zshrc)
echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.zshrc
```

**Option 3: Set for a specific Python session**

```python
import os

os.environ['GRAPHITI_TELEMETRY_ENABLED'] = 'false'

# Then initialize Graphiti as usual
from graphiti_core import Graphiti

graphiti = Graphiti(...)
```

Telemetry is automatically disabled during test runs (when `pytest` is detected).

### Technical Details

- Telemetry uses PostHog for anonymous analytics collection
- All telemetry operations are designed to fail silently - they will never interrupt your application or affect Graphiti
  functionality
- The anonymous ID is stored locally and is not tied to any personal information

## Contributing

We encourage and appreciate all forms of contributions, whether it's code, documentation, addressing GitHub Issues, or
helping others on GitHub. For detailed guidelines on code contributions, please refer
to [CONTRIBUTING](CONTRIBUTING.md).

## Support

Open a [GitHub issue](https://github.com/getzep/graphiti/issues) to ask questions, report bugs, or discuss Graphiti.

---

### Overview (`mcp_server/README.md`)

# Graphiti MCP Server

Graphiti is a framework for building and querying temporally-aware knowledge graphs, specifically tailored for AI agents
operating in dynamic environments. Unlike traditional retrieval-augmented generation (RAG) methods, Graphiti
continuously integrates user interactions, structured and unstructured enterprise data, and external information into a
coherent, queryable graph. The framework supports incremental data updates, efficient retrieval, and precise historical
queries without requiring complete graph recomputation, making it suitable for developing interactive, context-aware AI
applications.

> **Security:** This server is a reference implementation. Do not use the server as a production deployment. The server has no authentication, no authorization, and no prompt-injection protection. These omissions are intentional. Any caller can read, change, or delete all graph data. Do not expose the server to an untrusted network. For production options, see [Reference MCP and REST servers](../README.md#reference-mcp-and-rest-servers). For the security scope, see the [security policy scope](../SECURITY.md#scope).

This is an experimental Model Context Protocol (MCP) server implementation for Graphiti. The MCP server exposes
Graphiti's key functionality through the MCP protocol, allowing AI assistants to interact with Graphiti's knowledge
graph capabilities.

To expose the server on all interfaces, pass `--host 0.0.0.0` or set `server.host`, or change the host-side port
mapping in Docker Compose; the server has no authentication.

## Features

The Graphiti MCP server provides comprehensive knowledge graph capabilities:

- **Episode Management**: Add, retrieve, and delete episodes (text, messages, or JSON data)
- **Entity Management**: Search and manage entity nodes and relationships in the knowledge graph
- **Search Capabilities**: Search for facts (edges) and node summaries using semantic and hybrid search
- **Group Management**: Organize and manage groups of related data with group_id filtering
- **Graph Maintenance**: Clear the graph and rebuild indices
- **Graph Database Support**: Multiple backend options including FalkorDB (default) and Neo4j
- **Multiple LLM Providers**: Support for OpenAI, Anthropic, Gemini, Groq, and Azure OpenAI
- **Multiple Embedding Providers**: Support for OpenAI, Voyage, Sentence Transformers, and Gemini embeddings
- **Rich Entity Types**: Built-in entity types including Preferences, Requirements, Procedures, Locations, Events, Organizations, Documents, and more for structured knowledge extraction
- **HTTP Transport**: Default HTTP transport with MCP endpoint at `/mcp/` for broad client compatibility
- **Queue-based Processing**: Asynchronous episode processing with configurable concurrency limits

## Quick Start

### Clone the Graphiti GitHub repo

```bash
git clone https://github.com/getzep/graphiti.git
```

or

```bash
gh repo clone getzep/graphiti
```

### For Claude Desktop and other `stdio` only clients

1. Note the full path to this directory.

```
cd graphiti && pwd
```

2. Install the [Graphiti prerequisites](#prerequisites).

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.

### For Cursor and other HTTP-enabled clients

1. Change directory to the `mcp_server` directory

`cd graphiti/mcp_server`

2. Start the combined FalkorDB + MCP server using Docker Compose (recommended)

```bash
docker compose up
```

This starts both FalkorDB and the MCP server in a single container.

**Alternative**: Run with separate containers using Neo4j:
```bash
docker compose -f docker/docker-compose-neo4j.yml up
```

4. Point your MCP client to `http://localhost:8000/mcp/`

## Installation

### Prerequisites

1. Docker and Docker Compose (for the default FalkorDB setup)
2. OpenAI API key for LLM operations (or API keys for other supported LLM providers)
3. (Optional) Python 3.10+ if running the MCP server standalone with an external FalkorDB instance

### Setup

1. Clone the repository and navigate to the mcp_server directory
2. Use `uv` to create a virtual environment and install dependencies:

```bash
# Install uv if you don't have it already
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create a virtual environment and install dependencies in one step
uv sync

# Optional: Install additional LLM providers (anthropic, gemini, groq, voyage, sentence-transformers)
uv sync --extra providers
```

## Configuration

The server can be configured using a `config.yaml` file, environment variables, or command-line arguments (in order of precedence).

### Default Configuration

The MCP server comes with sensible defaults:
- **Transport**: HTTP (accessible at `http://localhost:8000/mcp/`)
- **Database**: FalkorDB (combined in single container with MCP server)
- **LLM**: OpenAI with model gpt-6-luna
- **Embedder**: OpenAI text-embedding-3-small

### Database Configuration

#### FalkorDB (Default)

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.

```yaml
database:
  provider: "falkordb"  # Default
  providers:
    falkordb:
      uri: "redis://localhost:6379"
      password: ""  # Optional
      database: "default_db"  # Optional
```

#### Neo4j

When you need a full-featured graph database, Neo4j is recommended:

```yaml
database:
  provider: "neo4j"
  providers:
    neo4j:
      uri: "bolt://localhost:7687"
      username: "neo4j"
      password: "your_password"
      database: "neo4j"  # Optional, defaults to "neo4j"
```

#### FalkorDB

FalkorDB is another graph database option based on Redis:

```yaml
database:
  provider: "falkordb"
  providers:
    falkordb:
      uri: "redis://localhost:6379"
      password: ""  # Optional
      database: "default_db"  # Optional
```

### Configuration File (config.yaml)

The server supports multiple LLM providers (OpenAI, Anthropic, Gemini, Groq) and embedders. Edit `config.yaml` to configure:

```yaml
server:
  transport: "http"  # Default. Options: stdio, http

llm:
  provider: "openai"  # or "anthropic", "gemini", "groq", "azure_openai"
  model: "gpt-6-luna"  # Default model
  # Optional for OpenAI-compatible providers: "json_schema" (default) or "json_object"
  structured_output_mode: "json_schema"

database:
  provider: "falkordb"  # Default. Options: "falkordb", "neo4j"
```

### Using Ollama for Local LLM

To use Ollama with the MCP server, configure it as an OpenAI-compatible endpoint:

```yaml
llm:
  provider: "openai"
  model: "gpt-oss:120b"  # or your preferred Ollama model
  api_base: "http://localhost:11434/v1"
  api_key: "ollama"  # dummy key required
  structured_output_mode: "json_object"  # use when json_schema is accepted but not enforced

embedder:
  provider: "sentence_transformers"  # recommended for local setup
  model: "all-MiniLM-L6-v2"
```

Make sure Ollama is running locally with: `ollama serve`

> [!IMPORTANT]
> Graphiti relies on structured (JSON) output for entity/edge extraction and deduplication, and reliability varies on
> small or local models. Prefer the most capable model your hardware can run — very small models frequently emit JSON
> that doesn't match the expected schema, which surfaces as ingestion failures. For background and the
> `structured_output_mode` (`json_schema` vs `json_object`) trade-off, see the core README's
> [Structured output and small models](../README.md#structured-output-and-small-models) section.
> If an OpenAI-compatible endpoint accepts `json_schema` but returns objects that miss fields such as
> `extracted_entities`, set `structured_output_mode: "json_object"` so Graphiti injects the schema into the prompt
> instead of relying on provider-side schema enforcement.

### Entity Types

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`:

**Available Entity Types:**

- **Preference**: User preferences, choices, opinions, or selections (prioritized for user-specific information)
- **Requirement**: Specific needs, features, or functionality that must be fulfilled
- **Procedure**: Standard operating procedures and sequential instructions
- **Location**: Physical or virtual places where activities occur
- **Event**: Time-bound activities, occurrences, or experiences
- **Person**: An individual human referenced in the content
- **Organization**: Companies, institutions, groups, or formal entities
- **Document**: Information content in various forms (books, articles, reports, videos, etc.)
- **Topic**: Subject of conversation, interest, or knowledge domain (used as a fallback)
- **Object**: Physical items, tools, devices, or possessions (used as a fallback)

These entity types are defined in `config.yaml` and can be customized by modifying the descriptions:

```yaml
graphiti:
  entity_types:
    - name: "Preference"
      description: "User preferences, choices, opinions, or selections"
    - name: "Requirement"
      description: "Specific needs, features, or functionality"
    # ... additional entity types
```

The MCP server automatically uses these entity types during episode ingestion to extract and structure information from conversations and documents.

> **Upgrade note:** These built-in types are now registered as rich models with typed attributes (e.g. a `description`), so extraction stores those attributes on entities. The shipped default `config.yaml` enables them, so an existing deployment on the default config switches from attribute-free to rich-attribute extraction. To keep the previous behavior, set `graphiti.entity_types` to an empty list (or remove the entries).

### Environment Variables

The `config.yaml` file supports environment variable expansion using `${VAR_NAME}` or `${VAR_NAME:default}` syntax. Key variables:

- `NEO4J_URI`: URI for the Neo4j database (default: `bolt://localhost:7687`)
- `NEO4J_USER`: Neo4j username (default: `neo4j`)
- `NEO4J_PASSWORD`: Neo4j password (default: `demodemo`)
- `NEO4J_DATABASE`: Neo4j database name (default: `neo4j`)
- `OPENAI_API_KEY`: OpenAI API key (required for OpenAI LLM/embedder)
- `ANTHROPIC_API_KEY`: Anthropic API key (for Claude models)
- `GOOGLE_API_KEY`: Google API key (for Gemini models)
- `GROQ_API_KEY`: Groq API key (for Groq models)
- `AZURE_OPENAI_API_KEY`: Azure OpenAI API key
- `AZURE_OPENAI_ENDPOINT`: Azure OpenAI endpoint URL
- `AZURE_OPENAI_DEPLOYMENT`: Azure OpenAI deployment name
- `AZURE_OPENAI_EMBEDDINGS_ENDPOINT`: Optional Azure OpenAI embeddings endpoint URL
- `AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT`: Optional Azure OpenAI embeddings deployment name
- `AZURE_OPENAI_API_VERSION`: Optional Azure OpenAI API version
- `USE_AZURE_AD`: Optional use Azure Managed Identities for authentication
- `SEMAPHORE_LIMIT`: Episode processing concurrency. See [Concurrency and LLM Provider 429 Rate Limit Errors](#concurrency-and-llm-provider-429-rate-limit-errors)

You can set these variables in a `.env` file in the project directory.

## Running the Server

### Default Setup (FalkorDB Combined Container)

To run the Graphiti MCP server with the default FalkorDB setup:

```bash
docker compose up
```

This starts a single container with:
- HTTP transport on `http://localhost:8000/mcp/`
- FalkorDB graph database on `localhost:6379`
- FalkorDB web UI on `http://localhost:3000`
- OpenAI LLM with gpt-6-luna model

### Running with Neo4j

#### Option 1: Using Docker Compose

The easiest way to run with Neo4j is using the provided Docker Compose configuration:

```bash
# This starts both Neo4j and the MCP server
docker compose -f docker/docker-compose.neo4j.yaml up
```

#### Option 2: Direct Execution with Existing Neo4j

If you have Neo4j already running:

```bash
# Set environment variables
export NEO4J_URI="bolt://localhost:7687"
export NEO4J_USER="neo4j"
export NEO4J_PASSWORD="your_password"

# Run with Neo4j
uv run main.py --database-provider neo4j
```

Or use the Neo4j configuration file:

```bash
uv run main.py --config config/config-docker-neo4j.yaml
```

### Running with FalkorDB

#### Option 1: Using Docker Compose

```bash
# This starts both FalkorDB (Redis-based) and the MCP server
docker compose -f docker/docker-compose.falkordb.yaml up
```

#### Option 2: Direct Execution with Existing FalkorDB

```bash
# Set environment variables
export FALKORDB_URI="redis://localhost:6379"
export FALKORDB_PASSWORD=""  # If password protected

# Run with FalkorDB
uv run main.py --database-provider falkordb
```

Or use the FalkorDB configuration file:

```bash
uv run main.py --config config/config-docker-falkordb.yaml
```

### Available Command-Line Arguments

- `--config`: Path to YAML configuration file (default: config.yaml)
- `--llm-provider`: LLM provider to use (openai, anthropic, gemini, groq, azure_openai)
- `--embedder-provider`: Embedder provider to use (openai, azure_openai, gemini, voyage)
- `--database-provider`: Database provider to use (falkordb, neo4j) - default: falkordb
- `--model`: Model name to use with the LLM client
- `--temperature`: Temperature setting for the LLM (0.0-2.0)
- `--transport`: Choose the transport method (http or stdio, default: http)
- `--group-id`: Set a namespace for the graph (optional). If not provided, defaults to "main"
- `--destroy-graph`: If set, destroys all Graphiti graphs on startup

### Concurrency and LLM Provider 429 Rate Limit Errors

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.

**Default:** `SEMAPHORE_LIMIT=10` (suitable for OpenAI Tier 3, mid-tier Anthropic)

#### Tuning Guidelines by LLM Provider

**OpenAI:**
- Tier 1 (free): 3 RPM → `SEMAPHORE_LIMIT=1-2`
- Tier 2: 60 RPM → `SEMAPHORE_LIMIT=5-8`
- Tier 3: 500 RPM → `SEMAPHORE_LIMIT=10-15`
- Tier 4: 5,000 RPM → `SEMAPHORE_LIMIT=20-50`

**Anthropic:**
- Default tier: 50 RPM → `SEMAPHORE_LIMIT=5-8`
- High tier: 1,000 RPM → `SEMAPHORE_LIMIT=15-30`

**Azure OpenAI:**
- Consult your quota in Azure Portal and adjust accordingly
- Start conservative and increase gradually

**Ollama (local):**
- Hardware dependent → `SEMAPHORE_LIMIT=1-5`
- Monitor CPU/GPU usage and adjust

#### Symptoms

- **Too high**: 429 rate limit errors, increased API costs from parallel processing
- **Too low**: Slow episode throughput, underutilized API quota

#### Monitoring

- Watch logs for `429` rate limit errors
- Monitor episode processing times in server logs
- Check your LLM provider's dashboard for actual request rates
- Track token usage and costs

Set this in your `.env` file:
```bash
SEMAPHORE_LIMIT=10  # Adjust based on your LLM provider tier
```

### Docker Deployment

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.

A pre-built Graphiti MCP container is available at: `zepai/knowledge-graph-mcp`

#### Environment Configuration

Before running Docker Compose, configure your API keys using a `.env` file (recommended):

1. **Create a .env file in the mcp_server directory**:
   ```bash
   cd graphiti/mcp_server
   cp .env.example .env
   ```

2. **Edit the .env file** to set your API keys:
   ```bash
   # Required - at least one LLM provider API key
   OPENAI_API_KEY=your_openai_api_key_here

   # Optional - other LLM providers
   ANTHROPIC_API_KEY=your_anthropic_key
   GOOGLE_API_KEY=your_google_key
   GROQ_API_KEY=your_groq_key

   # Optional - embedder providers
   VOYAGE_API_KEY=your_voyage_key
   ```

**Important**: The `.env` file must be in the `mcp_server/` directory (the parent of the `docker/` subdirectory).

#### Running with Docker Compose

**All commands must be run from the `mcp_server` directory** to ensure the `.env` file is loaded correctly:

```bash
cd graphiti/mcp_server
```

##### Option 1: FalkorDB Combined Container (Default)

Single container with both FalkorDB and MCP server - simplest option:

```bash
docker compose up
```

##### Option 2: Neo4j Database

Separate containers with Neo4j and MCP server:

```bash
docker compose -f docker/docker-compose-neo4j.yml up
```

Default Neo4j credentials:
- Username: `neo4j`
- Password: `demodemo`
- Bolt URI: `bolt://neo4j:7687`
- Browser UI: `http://localhost:7474`

##### Option 3: FalkorDB with Separate Containers

Alternative setup with separate FalkorDB and MCP server containers:

```bash
docker compose -f docker/docker-compose-falkordb.yml up
```

FalkorDB configuration:
- Redis port: `6379`
- Web UI: `http://localhost:3000`
- Connection: `redis://falkordb:6379`

#### Accessing the MCP Server

Once running, the MCP server is available at:
- **HTTP endpoint**: `http://localhost:8000/mcp/`
- **Health check**: `http://localhost:8000/health`

#### Running Docker Compose from a Different Directory

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:

```yaml
# Change this line in the docker-compose file:
env_file:
  - path: ../.env    # When running from mcp_server/

# To this:
env_file:
  - path: .env       # When running from mcp_server/docker/
```

However, **running from the `mcp_server/` directory is recommended** to avoid confusion.

## Integrating with MCP Clients

### VS Code / GitHub Copilot

VS Code with GitHub Copilot Chat extension supports MCP servers. Add to your VS Code settings (`.vscode/mcp.json` or global settings):

```json
{
  "mcpServers": {
    "graphiti": {
      "uri": "http://localhost:8000/mcp/",
      "transport": {
        "type": "http"
      }
    }
  }
}
```

### Other MCP Clients

To use the Graphiti MCP server with other MCP-compatible clients, configure it to connect to the server:

> [!IMPORTANT]
> You will need the Python package manager, `uv` installed. Please refer to the [`uv` install instructions](https://docs.astral.sh/uv/getting-started/installation/).
>
> Ensure that you set the full path to the `uv` binary and your Graphiti project folder.

```json
{
  "mcpServers": {
    "graphiti-memory": {
      "transport": "stdio",
      "command": "/Users/<user>/.local/bin/uv",
      "args": [
        "run",
        "--isolated",
        "--directory",
        "/Users/<user>>/dev/zep/graphiti/mcp_server",
        "--project",
        ".",
        "main.py",
        "--transport",
        "stdio"
      ],
      "env": {
        "NEO4J_URI": "bolt://localhost:7687",
        "NEO4J_USER": "neo4j",
        "NEO4J_PASSWORD": "password",
        "OPENAI_API_KEY": "sk-XXXXXXXX",
        "MODEL_NAME": "gpt-6-luna"
      }
    }
  }
}
```

For HTTP transport (default), you can use this configuration:

```json
{
  "mcpServers": {
    "graphiti-memory": {
      "transport": "http",
      "url": "http://localhost:8000/mcp/"
    }
  }
}
```

## Available Tools

The Graphiti MCP server exposes the following tools:

- `add_memory`: Add an episode to the knowledge graph (supports text, JSON, and message formats).
  Supports the bi-temporal `reference_time`, `excluded_entity_types`, `custom_extraction_instructions`,
  `previous_episode_uuids`, `update_communities`, and `saga` / `saga_previous_episode_uuid`.
- `add_triplet`: Add a single fact (source entity -> fact -> target entity) directly, bypassing extraction.
- `search_nodes`: Search the knowledge graph for relevant entities; supports `entity_types` and `center_node_uuid`.
- `search_memory_facts`: Search for relevant facts (edges); supports `edge_types`, `center_node_uuid`,
  and `valid_at` / `invalid_at` date-range filters.
- `summarize_saga`: Generate or refresh the running summary of a saga's episodes.
- `build_communities`: Detect entity communities and produce higher-level community summaries.
- `get_episode_entities`: Trace provenance — the entities and facts created by specific episode UUIDs.
  Accepts an optional `group_id`; defaults to the configured group.
- `delete_entity_edge`: Delete an entity edge from the knowledge graph.
  Accepts an optional `group_id`; defaults to the configured group.
- `delete_episode`: Delete an episode and cascade-delete the entities/facts it solely created.
  Accepts an optional `group_id`; defaults to the configured group.
- `get_entity_edge`: Get an entity edge by its UUID. Accepts an optional `group_id`; defaults to
  the configured group.
- `get_episodes`: Get the most recent episodes for one or more groups (`group_ids`).
- `clear_graph`: Clear all data from the knowledge graph for the given group(s).
- `get_status`: Get the status of the Graphiti MCP server and database connection.

On FalkorDB, each `group_id` is stored in its own graph. Pass the same `group_id` that you used with
`add_memory` when you read, delete, or clear data for that group. Without a `group_id`, tools use the
server's configured default group.

Custom entity types and edge (fact) types — including which edge types may connect which entity types —
can be configured under the `graphiti` section of `config/config.yaml`. See the `entity_types`,
`edge_types`, and `edge_type_map` keys there.

## Working with JSON Data

The Graphiti MCP server can process structured JSON data through the `add_episode` tool with `source="json"`. This
allows you to automatically extract entities and relationships from structured data:

```

add_episode(
name="Customer Profile",
episode_body="{\"company\": {\"name\": \"Acme Technologies\"}, \"products\": [{\"id\": \"P001\", \"name\": \"CloudSync\"}, {\"id\": \"P002\", \"name\": \"DataMiner\"}]}",
source="json",
source_description="CRM data"
)

```

## Integrating with the Cursor IDE

To integrate the Graphiti MCP Server with the Cursor IDE, follow these steps:

1. Run the Graphiti MCP server using the default HTTP transport:

```bash
uv run main.py --group-id <your_group_id>
```

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.

or

```bash
docker compose up
```

2. Configure Cursor to connect to the Graphiti MCP server.

```json
{
  "mcpServers": {
    "graphiti-memory": {
      "url": "http://localhost:8000/mcp/"
    }
  }
}
```

3. Add the Graphiti rules to Cursor's User Rules. See [cursor_rules.md](cursor_rules.md) for details.

4. Kick off an agent session in Cursor.

The integration enables AI assistants in Cursor to maintain persistent memory through Graphiti's knowledge graph
capabilities.

## Integrating with Claude Desktop (Docker MCP Server)

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`.

1.  **Run the Graphiti MCP server**:

    ```bash
    docker compose up
    # Or run directly with uv:
    uv run main.py
    ```

2.  **(Optional) Install `mcp-remote` globally**:
    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.

    ```bash
    npm install mcp-remote
    ```

3.  **Configure Claude Desktop**:
    Open your Claude Desktop configuration file (usually `claude_desktop_config.json`) and add or modify the `mcpServers` section as follows:

    ```json
    {
      "mcpServers": {
        "graphiti-memory": {
          // You can choose a different name if you prefer
          "command": "npx", // Or the full path to mcp-remote if npx is not in your PATH
          "args": [
            "mcp-remote",
            "http://localhost:8000/mcp/" // The Graphiti server's HTTP endpoint
          ]
        }
      }
    }
    ```

    If you already have an `mcpServers` entry, add `graphiti-memory` (or your chosen name) as a new key within it.

4.  **Restart Claude Desktop** for the changes to take effect.

## Requirements

- Python 3.10 or higher
- OpenAI API key (for LLM operations and embeddings) or other LLM provider API keys
- MCP-compatible client
- Docker and Docker Compose (for the default FalkorDB combined container)
- (Optional) Neo4j database (version 5.26 or later) if not using the default FalkorDB setup

## Telemetry

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.

### What's Collected

- Anonymous identifier and system information (OS, Python version)
- Graphiti version and configuration choices (LLM provider, database backend, embedder type)
- **No personal data, API keys, or actual graph content is ever collected**

### How to Disable

To disable telemetry in the MCP server, set the environment variable:

```bash
export GRAPHITI_TELEMETRY_ENABLED=false
```

Or add it to your `.env` file:

```
GRAPHITI_TELEMETRY_ENABLED=false
```

For complete details about what's collected and why, see the [Telemetry section in the main Graphiti README](../README.md#telemetry).

## License

This project is licensed under the same license as the parent Graphiti project.

---

### Overview (`server/README.md`)

# graph-service

Graph service is a fast api server implementing the [graphiti](https://github.com/getzep/graphiti) package.

> **Security:** This server is a reference implementation. Do not use the server as a production deployment. The server has no authentication, no authorization, and no prompt-injection protection. These omissions are intentional. Any caller can read, change, or delete all graph data. Do not expose the server to an untrusted network. For production options, see [Reference MCP and REST servers](../README.md#reference-mcp-and-rest-servers). For the security scope, see the [security policy scope](../SECURITY.md#scope).

To expose the server on all interfaces, run Uvicorn with `--host 0.0.0.0` or change the host-side port mapping in
Docker Compose; the server has no authentication.

## Container Releases

The FastAPI server container is automatically built and published to Docker Hub when a `v*.*.*` tag is pushed (the same tags that publish `graphiti-core` to PyPI).

**Image:** `zepai/graphiti`

**Available tags:**
- `latest` - Latest stable release
- `0.30.2` - Specific version (matches graphiti-core version)

**Platforms:** linux/amd64, linux/arm64

The automated release workflow:
1. Triggers on `v*.*.*` tag pushes (and can be run manually via `workflow_dispatch`)
2. Waits for that `graphiti-core` version to be available on PyPI
3. Builds multi-platform Docker image
4. Tags with version number and `latest`
5. Pushes to Docker Hub

Only stable releases are built automatically (pre-release versions are skipped).

## Running Instructions

1. Ensure you have Docker and Docker Compose installed on your system.

2. Add `zepai/graphiti:latest` to your service setup

3. Make sure to pass the following environment variables to the service

   ```
   OPENAI_API_KEY=your_openai_api_key
   NEO4J_USER=your_neo4j_user
   NEO4J_PASSWORD=your_neo4j_password
   NEO4J_PORT=your_neo4j_port
   ```

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.

   An example of docker compose setup may look like this:

   ```yml
      version: '3.8'

      services:
      graph:
         image: zepai/graphiti:latest
         ports:
            - "127.0.0.1:8000:8000"
         
         environment:
            - OPENAI_API_KEY=${OPENAI_API_KEY}
            - NEO4J_URI=bolt://neo4j:${NEO4J_PORT}
            - NEO4J_USER=${NEO4J_USER}
            - NEO4J_PASSWORD=${NEO4J_PASSWORD}
      neo4j:
         image: neo4j:5.22.0
         
         ports:
            - "7474:7474"  # HTTP
            - "${NEO4J_PORT}:${NEO4J_PORT}"  # Bolt
         volumes:
            - neo4j_data:/data
         environment:
            - NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}

      volumes:
      neo4j_data:
   ```

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).

6. You may access the swagger docs at `http://localhost:8000/docs`. You may also access redocs at `http://localhost:8000/redoc`.

7. You may also access the neo4j browser at `http://localhost:7474` (the port depends on the neo4j instance you are using).

---

### Overview (`.github/intake/README.md`)

# Issue / PR intake automation

A fully deterministic triage bot for issues and pull requests. `decide.py`
fetches the item through the GitHub API and computes a decision from objective
facts only — the issue-form sections, the labels the forms apply, the changed
file paths, and the `Fixes #<number>` references in the body. `apply.py` turns
that decision into allowlisted labels and one templated "sticky" comment. No
LLM is involved and no API key secret is needed: both scripts run on the
standard `GITHUB_TOKEN` and the Python standard library alone.

## One-time setup

1. **Create the labels** (required before the issue forms or bot run — GitHub
   silently drops form labels that do not exist):

   ```bash
   bash .github/scripts/setup-triage-labels.sh
   ```

2. **(Optional) repository variable** to disable intake without editing code:

   | Variable | Default | Purpose |
   |----------|---------|---------|
   | `INTAKE_ENABLED` | unset (enabled) | Set to `false` to disable all intake + stale workflows (kill switch). |

## How it runs

- **`.github/workflows/issue-intake.yml`** — on issue open/edit/reopen. One
  job: `decide.py --kind issue` then `apply.py --write`.
- **`.github/workflows/pr-intake.yml`** — on PR open/sync/reopen/edit, for
  same-repo and fork branches. It uses `pull_request_target` so the token can
  label fork PRs, checks out the base-branch scripts (never the PR's), and
  reads the PR through the API, so it never runs PR-authored code.
- **`.github/workflows/ai-moderator.yml`** — spam and AI-generated-content
  detection for new issues and comments (the `github/ai-moderator` action).
- **`.github/workflows/stale.yml`** — daily; warns then closes items that keep
  any `needs-*` label (`needs-info`, `needs-issue`, `needs-rfc`, `needs-tests`,
  `needs-rework`) for 14 days. `rfc-approved` and `security` items are exempt.

Trusted authors (`OWNER`/`MEMBER` author association) and draft pull requests
are skipped: `decide.py` writes the empty decision `{}`, which `apply.py`
treats as a clean no-op.

## Issue rules

The issue forms (`.github/ISSUE_TEMPLATE/*.yml`) render into `### <Label>`
sections in the body. A section counts as missing when it is absent, blank, or
`_No response_`.

- **`category`**: the first of `bug`, `feature`, `documentation`, `question`
  already on the issue (the form sets it), else `other`.
- **`areas`**: from the `Affected component` (or `Component`) dropdown —
  `graphiti-core` → `scope:core`, `MCP server` → `scope:mcp`, `REST server` →
  `scope:service`, `Documentation or examples` → `scope:docs`,
  `CI, Docker, or release` → `scope:ci`. Other values and a missing section
  give no scope; a `documentation` issue with no scope defaults to
  `scope:docs`.
- **Required sections** (missing ones produce `needs-info` and land in
  `missing_fields`):

  | Category | Required |
  |----------|----------|
  | bug | description, reproduction, expected, actual, environment |
  | feature | problem, outcome |
  | question | goal, attempted, environment |
  | documentation | location, problem |

- **`needs-rfc`**: a feature whose `Size` is `Large feature requiring design
  approval` gets `needs-rfc` unless the issue already has `rfc-approved`, and
  `proposal`/`alternatives`/`impact` become required as well.
- **`comment_id`**: `ask_rfc_fields` when `needs-rfc`; `ask_repro` for a bug
  missing fields; `ask_info` for anything else missing fields; else none.

## Pull request rules

- **`areas`**: derived from the changed file paths (see `PATH_SCOPE_RULES`).
- **`needs-issue`**: no linked issue (`Fixes #<number>`) → `needs-issue` and
  the `linked-issue` missing field. A number that 404s or resolves to a pull
  request does not count.
- **`category`**: `feature`/`bug`/`documentation` when a linked issue carries
  that label, else `feature` when the template's Feature checkbox is ticked,
  else `other`.
- **`needs-rfc`**: a feature PR whose linked issue lacks `rfc-approved`.
- **`needs-tests`**: the diff touches code but no test file, and the
  `Tests are not applicable` checkbox is not ticked.
- **`needs-rework`**: `needs-issue` and `needs-tests` together.
- **`comment_id`**: `pr_needs_rework` > `pr_needs_rfc` > `pr_needs_tests`.

## Files

| File | Role |
|------|------|
| `decide.py` | Read-only: fetch item, compute the deterministic decision, write JSON. Stdlib only. |
| `apply.py` | Deterministic: validate the decision, apply allowlisted labels + one templated sticky comment. Stdlib only. |
| `decision.schema.json` | The decision contract and the **single source of truth** for the label taxonomy (apply.py derives its allowlist from it). |
| `templates/*.md` | The only text the bot can post; substitutions come from closed sets. |

Tests: `tests/intake/` (run by `unit_tests.yml`). The workflow guard tests in
`test_workflows.py` encode the security invariants — keep them passing.

---

### Overview (`examples/azure-openai/README.md`)

# Azure OpenAI with Neo4j Example

This example demonstrates how to use Graphiti with Azure OpenAI and Neo4j to build a knowledge graph.

## Prerequisites

- Python 3.10+
- Neo4j database (running locally or remotely)
- Azure OpenAI subscription with deployed models

## Setup

### 1. Install Dependencies

```bash
uv sync
```

### 2. Configure Environment Variables

Copy the `.env.example` file to `.env` and fill in your credentials:

```bash
cd examples/azure-openai
cp .env.example .env
```

Edit `.env` with your actual values:

```env
# Neo4j connection settings
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your-password

# Azure OpenAI settings
AZURE_OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com
AZURE_OPENAI_API_KEY=your-api-key-here
AZURE_OPENAI_DEPLOYMENT=gpt-5-mini
AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
```

### 3. Azure OpenAI Model Deployments

This example requires two Azure OpenAI model deployments:

1. **Chat Completion Model**: Used for entity extraction and relationship analysis
   - Set the deployment name in `AZURE_OPENAI_DEPLOYMENT`

2. **Embedding Model**: Used for semantic search
   - Set the deployment name in `AZURE_OPENAI_EMBEDDING_DEPLOYMENT`

### 4. Neo4j Setup

Make sure Neo4j is running and accessible at the URI specified in your `.env` file.

For local development:
- Download and install [Neo4j Desktop](https://neo4j.com/download/)
- Create a new database
- Start the database
- Use the credentials in your `.env` file

## Running the Example

```bash
cd examples/azure-openai
uv run azure_openai_neo4j.py
```

## What This Example Does

1. **Initialization**: Sets up connections to Neo4j and Azure OpenAI
2. **Adding Episodes**: Ingests text and JSON data about California politics
3. **Basic Search**: Performs hybrid search combining semantic similarity and BM25 retrieval
4. **Center Node Search**: Reranks results based on graph distance to a specific node
5. **Cleanup**: Properly closes database connections

## Key Concepts

### Azure OpenAI Integration

The example shows how to configure Graphiti to use Azure OpenAI with the OpenAI v1 API:

```python
# Initialize Azure OpenAI client using the standard OpenAI client
# with Azure's v1 API endpoint
azure_client = AsyncOpenAI(
    base_url=f"{azure_endpoint}/openai/v1/",
    api_key=azure_api_key,
)

# Create LLM and Embedder clients
llm_client = AzureOpenAILLMClient(
    azure_client=azure_client,
    config=LLMConfig(model=azure_deployment, small_model=azure_deployment)
)
embedder_client = AzureOpenAIEmbedderClient(
    azure_client=azure_client,
    model=azure_embedding_deployment
)

# Initialize Graphiti with custom clients
graphiti = Graphiti(
    neo4j_uri,
    neo4j_user,
    neo4j_password,
    llm_client=llm_client,
    embedder=embedder_client,
)
```

**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/`.

### Episodes

Episodes are the primary units of information in Graphiti. They can be:
- **Text**: Raw text content (e.g., transcripts, documents)
- **JSON**: Structured data with key-value pairs

### Hybrid Search

Graphiti combines multiple search strategies:
- **Semantic Search**: Uses embeddings to find semantically similar content
- **BM25**: Keyword-based text retrieval
- **Graph Traversal**: Leverages relationships between entities

## Troubleshooting

### Azure OpenAI API Errors

- Verify your endpoint URL is correct (should end in `.openai.azure.com`)
- Check that your API key is valid
- Ensure your deployment names match actual deployments in Azure
- Verify API version is supported by your deployment

### Neo4j Connection Issues

- Ensure Neo4j is running
- Check firewall settings
- Verify credentials are correct
- Check URI format (should be `bolt://` or `neo4j://`)

## Next Steps

- Explore other search recipes in `graphiti_core/search/search_config_recipes.py`
- Try different episode types and content
- Experiment with custom entity definitions
- Add more episodes to build a larger knowledge graph

## Related Examples

- `examples/quickstart/` - Basic Graphiti usage with OpenAI
- `examples/podcast/` - Processing longer content
- `examples/ecommerce/` - Domain-specific knowledge graphs

---

### Overview (`examples/gliner2/README.md`)

# GLiNER2 Hybrid LLM Client Example (Experimental)

> **Note:** The `GLiNER2Client` is experimental and may change in future releases.

This example demonstrates using [GLiNER2](https://github.com/fastino-ai/GLiNER2) as a hybrid LLM client for Graphiti. GLiNER2 handles entity extraction (NER) locally on CPU, while a general-purpose LLM client handles edge/fact extraction, deduplication, summarization, and other reasoning tasks.

- Paper: [GLiNER2: An Efficient Multi-Task Information Extraction System with Schema-Driven Interface](https://arxiv.org/abs/2507.18546)
- Models on HuggingFace:
  - [fastino/gliner2-base-v1](https://huggingface.co/fastino/gliner2-base-v1) (205M params)
  - [fastino/gliner2-large-v1](https://huggingface.co/fastino/gliner2-large-v1) (340M params)
  - [fastino/gliner2-multi-v1](https://huggingface.co/fastino/gliner2-multi-v1) (multilingual)

## Prerequisites

- Python 3.11+
- Neo4j 5.26+ ([Neo4j Desktop](https://neo4j.com/download/) or Docker)
- An LLM provider API key (Google, OpenAI, Anthropic, etc.)

## Setup

```bash
# Install graphiti with the gliner2 extra
pip install graphiti-core[gliner2]

# Copy and configure environment variables
cp .env.example .env
```

The GLiNER2 model weights are downloaded automatically on first run.

## LLM and Embedding Providers

The example uses Google Gemini (`gemini-2.5-flash-lite`) for the LLM and embeddings, but `GLiNER2Client` accepts any Graphiti `LLMClient`. To swap providers, replace `GeminiClient` and `GeminiEmbedder` with the equivalent from another provider:

- `graphiti_core.llm_client.openai_client.OpenAIClient`
- `graphiti_core.llm_client.anthropic_client.AnthropicClient`
- `graphiti_core.llm_client.groq_client.GroqClient`
- `graphiti_core.embedder.openai.OpenAIEmbedder`
- `graphiti_core.embedder.voyage.VoyageAIEmbedder`

## Configuration

| Parameter | Description | Default |
|---|---|---|
| `threshold` | GLiNER2 confidence threshold (0.0-1.0). Higher values reduce spurious extractions. | `0.5` |
| `GLINER2_MODEL` | HuggingFace model ID | `fastino/gliner2-large-v1` |

## Running

```bash
python gliner2_neo4j.py
```

---

### Overview (`examples/jev/README.md`)

# Jev dedupe with FalkorDB

This example routes node deduplication and edge resolution to Jev. OpenAI
`gpt-6-luna` handles extraction, summaries, and all other prompts. The example
adds four episodes in order. The episodes show a name variant, a repeated
fact, and a transfer of responsibility for the customer billing platform from
Alice Chen to Priya Patel.

## Prerequisites

- Python 3.11+
- FalkorDB
- An OpenAI API key in `OPENAI_API_KEY`
- A TypeSafe API key in `JEV_API_KEY`

Start FalkorDB with Docker:

```bash
docker run -p 6379:6379 falkordb/falkordb:latest
```

Copy `.env.example` to `.env` and set both API keys. The example uses
`localhost:6379` by default. Set `FALKORDB_HOST`, `FALKORDB_PORT`,
`FALKORDB_USERNAME`, or `FALKORDB_PASSWORD` to use other connection settings.

## When to use a decision model

Jev is a decision model. It answers typed questions with probabilities. It does
not generate text. Use a general LLM as the default model. Route only
`dedupe_nodes.nodes` and `dedupe_edges.resolve_edge` to Jev. Do not use Jev for
extraction, summaries, or other generation tasks.

## Prompt routing

| Prompt | Model |
|---|---|
| Extraction, summaries, and all other prompts | OpenAI `gpt-6-luna` |
| `dedupe_nodes.nodes` | Jev `jev-latest` |
| `dedupe_edges.resolve_edge` | Jev `jev-latest` |

The example uses the default OpenAI embedder. TypeSafe hosts the Jev API.
Graphiti sends dedupe prompt content to TypeSafe.

## Expected output

One run showed Alice's billing-platform fact with
`invalid_at=2025-01-10T00:00:00+00:00` and an `expired_at` value. Priya's
replacement fact was active. The contradiction probability was `0.89` for
Alice's fact and `0.04` for the Acme fact. The run made 2
`dedupe_nodes.nodes` calls and 3 `dedupe_edges.resolve_edge` calls. The final
node list showed one Alice Chen entity.

Extraction output can change between runs. If the model does not extract
Alice's billing-platform fact, or if it writes the change as a separate
termination fact, Alice's fact stays active. The current Graphiti contradiction
rule uses only the new fact's `valid_at` value.

## Run the example

From the repository root, install the project dependencies:

```bash
uv sync --extra dev
```

Then run the example from this directory:

```bash
cd examples/jev
uv run python jev_dedupe_falkordb.py
```

The output lists the entities and facts for each episode. It then lists all
facts with their invalidation or expiration times and the final entity nodes.
The example does not call `Graphiti.search`.

---

### Overview (`examples/openai_decisions/README.md`)

# OpenAI Decisions dedupe with FalkorDB

This example routes node deduplication and edge resolution to OpenAI Decisions.
OpenAI `gpt-6-luna` Chat Completions handles extraction, summaries, and all
other prompts. The example adds four episodes in order. The episodes show a
name variant, a repeated fact, and a transfer of responsibility for the
customer billing platform from Alice Chen to Priya Patel.

## Prerequisites

- Python 3.11+
- FalkorDB
- An OpenAI API key in `OPENAI_API_KEY`

Start FalkorDB with Docker:

```bash
docker run -p 6379:6379 falkordb/falkordb:latest
```

Copy `.env.example` to `.env` and set the API key. The example uses
`localhost:6379` by default. Set `FALKORDB_HOST`, `FALKORDB_PORT`,
`FALKORDB_USERNAME`, or `FALKORDB_PASSWORD` to use other connection settings.

## When to use a decision model

OpenAI Decisions is a decision model. It answers typed questions with
probabilities. It does not generate text. Use a general LLM as the default
model. Route only `dedupe_nodes.nodes` and `dedupe_edges.resolve_edge` to the
decision model. Do not use it for extraction, summaries, or other generation
tasks.

## Prompt routing

| Prompt | Model |
|---|---|
| Extraction, summaries, and all other prompts | OpenAI `gpt-6-luna` |
| `dedupe_nodes.nodes` | OpenAI Decisions `gpt-6-luna` |
| `dedupe_edges.resolve_edge` | OpenAI Decisions `gpt-6-luna` |

The example uses the default OpenAI embedder. OpenAI hosts the Decisions API.
Graphiti sends the dedupe state and typed questions to OpenAI.

## Expected output

One run showed Alice's billing-platform facts with
`invalid_at=2025-01-10T00:00:00+00:00` and `expired_at` values. Priya's
replacement fact was active. The contradiction probability was `1.0` for both
Alice facts. The run made 2 `dedupe_nodes.nodes` calls and 3
`dedupe_edges.resolve_edge` calls. The final node list showed one Alice Chen
entity.

Extraction output can change between runs. If the model does not extract
Alice's billing-platform fact, or if it writes the change as a separate
termination fact, Alice's fact stays active. The current Graphiti contradiction
rule uses only the new fact's `valid_at` value.

## Run the example

From the repository root, install the project dependencies:

```bash
uv sync --extra dev
```

Then run the example from this directory:

```bash
cd examples/openai_decisions
uv run python openai_decisions_dedupe_falkordb.py
```

The output lists the entities and facts for each episode. It then lists all
facts with their invalidation or expiration times and the final entity nodes.
The example does not call `Graphiti.search`.

---

### Overview (`examples/opentelemetry/README.md`)

# OpenTelemetry Stdout Tracing Example

Configure Graphiti with OpenTelemetry to output trace spans to stdout.

## Setup

```bash
uv sync
export OPENAI_API_KEY=your_api_key_here
uv run otel_stdout_example.py
```

## Configure OpenTelemetry with Graphiti

```python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# Set up OpenTelemetry with stdout exporter
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)

# Get tracer and pass to Graphiti
tracer = trace.get_tracer(__name__)
graphiti = Graphiti(
    graph_driver=kuzu_driver,
    tracer=tracer,
    trace_span_prefix='graphiti.example'
)
```

---

### Overview (`examples/quickstart/README.md`)

# Graphiti Quickstart Example

This example demonstrates the basic functionality of Graphiti, including:

1. Connecting to a Neo4j or FalkorDB database
2. Initializing Graphiti indices and constraints
3. Adding episodes to the graph
4. Searching the graph with semantic and keyword matching
5. Exploring graph-based search with reranking using the top search result's source node UUID
6. Performing node search using predefined search recipes

## Prerequisites

- Python 3.9+  
- OpenAI API key (set as `OPENAI_API_KEY` environment variable)  
- **For Neo4j**:
  - Neo4j Desktop installed and running  
  - A local DBMS created and started in Neo4j Desktop  
- **For FalkorDB**:
  - FalkorDB server running (see [FalkorDB documentation](https://docs.falkordb.com) for setup)
- **For Amazon Neptune**:
  - Amazon server running (see [Amazon Neptune documentation](https://aws.amazon.com/neptune/developer-resources/) for setup)


## Setup Instructions

1. Install the required dependencies:

```bash
pip install graphiti-core
```

2. Set up environment variables:

```bash
# Required for LLM and embedding
export OPENAI_API_KEY=your_openai_api_key

# Optional Neo4j connection parameters (defaults shown)
export NEO4J_URI=bolt://localhost:7687
export NEO4J_USER=neo4j
export NEO4J_PASSWORD=password

# Optional FalkorDB connection parameters (defaults shown)
export FALKORDB_URI=falkor://localhost:6379

# Optional Amazon Neptune connection parameters
NEPTUNE_HOST=your_neptune_host
NEPTUNE_PORT=your_port_or_8182
AOSS_HOST=your_aoss_host
AOSS_PORT=your_port_or_443

# To use a different database, modify the driver constructor in the script
```

TIP: For Amazon Neptune host string please use the following formats
* For Neptune Database: `neptune-db://<cluster endpoint>`
* For Neptune Analytics: `neptune-graph://<graph identifier>`

3. Run the example:

```bash
python quickstart_neo4j.py

# For FalkorDB
python quickstart_falkordb.py

# For Amazon Neptune
python quickstart_neptune.py
```

## What This Example Demonstrates

- **Graph Initialization**: Setting up the Graphiti indices and constraints in Neo4j, Amazon Neptune, or FalkorDB
- **Adding Episodes**: Adding text content that will be analyzed and converted into knowledge graph nodes and edges
- **Edge Search Functionality**: Performing hybrid searches that combine semantic similarity and BM25 retrieval to find relationships (edges)
- **Graph-Aware Search**: Using the source node UUID from the top search result to rerank additional search results based on graph distance
- **Node Search Using Recipes**: Using predefined search configurations like NODE_HYBRID_SEARCH_RRF to directly search for nodes rather than edges
- **Result Processing**: Understanding the structure of search results including facts, nodes, and temporal metadata

## Next Steps

After running this example, you can:

1. Modify the episode content to add your own information
2. Try different search queries to explore the knowledge extraction
3. Experiment with different center nodes for graph-distance-based reranking
4. Try other predefined search recipes from `graphiti_core.search.search_config_recipes`
5. Explore the more advanced examples in the other directories

## Troubleshooting

### "Graph not found: default_db" Error

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.

**Solution:**
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:

```python
# In quickstart_neo4j.py, change:
driver = Neo4jDriver(uri=neo4j_uri, user=neo4j_user, password=neo4j_password)

# To specify a different database:
driver = Neo4jDriver(uri=neo4j_uri, user=neo4j_user, password=neo4j_password, database="your_db_name")
```

## Understanding the Output

### Edge Search Results

The edge search results include EntityEdge objects with:

- UUID: Unique identifier for the edge
- Fact: The extracted fact from the episode
- Valid at/invalid at: Time period during which the fact was true (if available)
- Source/target node UUIDs: Connections between entities in the knowledge graph

### Node Search Results

The node search results include EntityNode objects with:

- UUID: Unique identifier for the node
- Name: The name of the entity
- Content Summary: A summary of the node's content
- Node Labels: The types of the node (e.g., Person, Organization)
- Created At: When the node was created
- Attributes: Additional properties associated with the node

---

### Overview (`mcp_server/docker/README.md`)

# Docker Deployment for Graphiti MCP Server

This directory contains Docker Compose configurations for running the Graphiti MCP server with graph database backends: FalkorDB (combined image) and Neo4j.

## Quick Start

```bash
# Default configuration (FalkorDB combined image)
docker-compose up

# Neo4j (separate containers)
docker-compose -f docker-compose-neo4j.yml up
```

## Environment Variables

Create a `.env` file in this directory with your API keys:

```bash
# Required
OPENAI_API_KEY=your-api-key-here

# Optional
GRAPHITI_GROUP_ID=main
SEMAPHORE_LIMIT=10

# Database-specific variables (see database sections below)
```

## Database Configurations

### FalkorDB (Combined Image)

**File:** `docker-compose.yml` (default)

The default configuration uses a combined Docker image that bundles both FalkorDB and the MCP server together for simplified deployment.

#### Configuration

```bash
# Environment variables
FALKORDB_URI=redis://localhost:6379  # Connection URI (services run in same container)
FALKORDB_PASSWORD=  # Password (default: empty)
FALKORDB_DATABASE=default_db  # Database name (default: default_db)
```

#### Accessing Services

- **FalkorDB (Redis):** redis://localhost:6379
- **FalkorDB Web UI:** http://localhost:3000
- **MCP Server:** http://localhost:8000

#### Data Management

**Backup:**
```bash
docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \
  tar czf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data .
```

**Restore:**
```bash
docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \
  tar xzf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data
```

**Clear Data:**
```bash
docker-compose down
docker volume rm mcp_server_falkordb_data
docker-compose up
```

#### Gotchas
- Both FalkorDB and MCP server run in the same container
- FalkorDB uses Redis persistence mechanisms (AOF/RDB)
- Default configuration has no password - add one for production
- Health check only monitors FalkorDB; MCP server startup visible in logs

See [README-falkordb-combined.md](README-falkordb-combined.md) for detailed information about the combined image.

### Neo4j

**File:** `docker-compose-neo4j.yml`

Neo4j runs as a separate container service with its own web interface.

#### Configuration

```bash
# Environment variables
NEO4J_URI=bolt://neo4j:7687  # Connection URI (default: bolt://neo4j:7687)
NEO4J_USER=neo4j  # Username (default: neo4j)
NEO4J_PASSWORD=demodemo  # Password (default: demodemo)
NEO4J_DATABASE=neo4j  # Database name (default: neo4j)
USE_PARALLEL_RUNTIME=false  # Enterprise feature (default: false)
```

#### Accessing Neo4j

- **Web Interface:** http://localhost:7474
- **Bolt Protocol:** bolt://localhost:7687
- **MCP Server:** http://localhost:8000

Default credentials: `neo4j` / `demodemo`

#### Data Management

**Backup:**
```bash
# Backup both data and logs volumes
docker run --rm -v docker_neo4j_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/neo4j-data-backup.tar.gz -C /data .
docker run --rm -v docker_neo4j_logs:/logs -v $(pwd):/backup alpine \
  tar czf /backup/neo4j-logs-backup.tar.gz -C /logs .
```

**Restore:**
```bash
# Restore both volumes
docker run --rm -v docker_neo4j_data:/data -v $(pwd):/backup alpine \
  tar xzf /backup/neo4j-data-backup.tar.gz -C /data
docker run --rm -v docker_neo4j_logs:/logs -v $(pwd):/backup alpine \
  tar xzf /backup/neo4j-logs-backup.tar.gz -C /logs
```

**Clear Data:**
```bash
docker-compose -f docker-compose-neo4j.yml down
docker volume rm docker_neo4j_data docker_neo4j_logs
docker-compose -f docker-compose-neo4j.yml up
```

#### Gotchas
- Neo4j takes 30+ seconds to start up - wait for the health check
- The web interface requires authentication even for local access
- Memory heap is configured for 512MB initial, 1GB max
- Page cache is set to 512MB
- Enterprise features like parallel runtime require a license

## Switching Between Databases

To switch from FalkorDB to Neo4j (or vice versa):

1. **Stop current setup:**
   ```bash
   docker-compose down  # Stop FalkorDB combined image
   # or
   docker-compose -f docker-compose-neo4j.yml down  # Stop Neo4j
   ```

2. **Start new database:**
   ```bash
   docker-compose up  # Start FalkorDB combined image
   # or
   docker-compose -f docker-compose-neo4j.yml up  # Start Neo4j
   ```

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.

## Troubleshooting

### Port Conflicts

If port 8000 is already in use:
```bash
# Find what's using the port
lsof -i :8000

# Change the port in docker-compose.yml
# Under ports section: "8001:8000"
```

### Container Won't Start

1. Check logs:
   ```bash
   docker-compose logs graphiti-mcp
   ```

2. Verify `.env` file exists and contains valid API keys:
   ```bash
   cat .env | grep API_KEY
   ```

3. Ensure Docker has enough resources allocated

### Database Connection Issues

**FalkorDB:**
- Test Redis connectivity: `docker compose exec graphiti-falkordb redis-cli ping`
- Check FalkorDB logs: `docker compose logs graphiti-falkordb`
- Verify both services started: Look for "FalkorDB is ready!" and "Starting MCP server..." in logs

**Neo4j:**
- Wait for health check to pass (can take 30+ seconds)
- Check Neo4j logs: `docker-compose -f docker-compose-neo4j.yml logs neo4j`
- Verify credentials match environment variables

**FalkorDB:**
- Test Redis connectivity: `redis-cli -h localhost ping`

### Data Not Persisting

1. Verify volumes are created:
   ```bash
   docker volume ls | grep docker_
   ```

2. Check volume mounts in container:
   ```bash
   docker inspect graphiti-mcp | grep -A 5 Mounts
   ```

3. Ensure proper shutdown:
   ```bash
   docker-compose down  # Not docker-compose down -v (which removes volumes)
   ```

### Performance Issues

**FalkorDB:**
- Adjust `SEMAPHORE_LIMIT` environment variable
- Monitor with: `docker stats graphiti-falkordb`
- Check Redis memory: `docker compose exec graphiti-falkordb redis-cli info memory`

**Neo4j:**
- Increase heap memory in docker-compose-neo4j.yml
- Adjust page cache size based on data size
- Check query performance in Neo4j browser

## Docker Resources

### Volumes

Each database configuration uses named volumes for data persistence:
- FalkorDB (combined): `falkordb_data`
- Neo4j: `neo4j_data`, `neo4j_logs`

### Networks

All configurations use the default bridge network. Services communicate using container names as hostnames.

### Resource Limits

No resource limits are set by default. To add limits, modify the docker-compose file:

```yaml
services:
  graphiti-mcp:
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
```

## Configuration Files

Each database has a dedicated configuration file in `../config/`:
- `config-docker-falkordb-combined.yaml` - FalkorDB combined image configuration
- `config-docker-neo4j.yaml` - Neo4j configuration

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).

---

### Overview (`mcp_server/tests/README.md`)

# Graphiti MCP Server Integration Tests

This directory contains a comprehensive integration test suite for the Graphiti MCP Server using the official Python MCP SDK.

## Overview

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.

## Test Organization

### Core Test Modules

- **`test_comprehensive_integration.py`** - Main integration test suite covering all MCP tools
- **`test_async_operations.py`** - Tests for concurrent operations and async patterns
- **`test_stress_load.py`** - Stress testing and load testing scenarios
- **`test_fixtures.py`** - Shared fixtures and test utilities
- **`test_mcp_integration.py`** - Original MCP integration tests
- **`test_configuration.py`** - Configuration loading and validation tests

### Test Categories

Tests are organized with pytest markers:

- `unit` - Fast unit tests without external dependencies
- `integration` - Tests requiring database and services
- `slow` - Long-running tests (stress/load tests)
- `requires_neo4j` - Tests requiring Neo4j
- `requires_falkordb` - Tests requiring FalkorDB
- `requires_openai` - Tests requiring OpenAI API key

## Installation

```bash
# Install test dependencies
uv add --dev pytest pytest-asyncio pytest-timeout pytest-xdist faker psutil

# Install MCP SDK
uv add mcp
```

## Running Tests

### Quick Start

```bash
# Run smoke tests (quick validation)
python tests/run_tests.py smoke

# Run integration tests with mock LLM
python tests/run_tests.py integration --mock-llm

# Run all tests
python tests/run_tests.py all
```

### Test Runner Options

```bash
python tests/run_tests.py [suite] [options]

Suites:
  unit          - Unit tests only
  integration   - Integration tests
  comprehensive - Comprehensive integration suite
  async         - Async operation tests
  stress        - Stress and load tests
  smoke         - Quick smoke tests
  all           - All tests

Options:
  --database    - Database backend (neo4j, falkordb)
  --mock-llm    - Use mock LLM for faster testing
  --parallel N  - Run tests in parallel with N workers
  --coverage    - Generate coverage report
  --skip-slow   - Skip slow tests
  --timeout N   - Test timeout in seconds
  --check-only  - Only check prerequisites
```

### Examples

```bash
# Quick smoke test with FalkorDB (default)
python tests/run_tests.py smoke

# Full integration test with Neo4j
python tests/run_tests.py integration --database neo4j

# Stress testing with parallel execution
python tests/run_tests.py stress --parallel 4

# Run with coverage
python tests/run_tests.py all --coverage

# Check prerequisites only
python tests/run_tests.py all --check-only
```

## Test Coverage

### Core Operations
- Server initialization and tool discovery
- Adding memories (text, JSON, message)
- Episode queue management
- Search operations (semantic, hybrid)
- Episode retrieval and deletion
- Entity and edge operations

### Async Operations
- Concurrent operations
- Queue management
- Sequential processing within groups
- Parallel processing across groups

### Performance Testing
- Latency measurement
- Throughput testing
- Batch processing
- Resource usage monitoring

### Stress Testing
- Sustained load scenarios
- Spike load handling
- Memory leak detection
- Connection pool exhaustion
- Rate limit handling

## Configuration

### Environment Variables

```bash
# Database configuration
export DATABASE_PROVIDER=falkordb  # or neo4j
export NEO4J_URI=bolt://localhost:7687
export NEO4J_USER=neo4j
export NEO4J_PASSWORD=graphiti
export FALKORDB_URI=redis://localhost:6379

# LLM configuration
export OPENAI_API_KEY=your_key_here  # or use --mock-llm

# Test configuration
export TEST_MODE=true
export LOG_LEVEL=INFO
```

### pytest.ini Configuration

The `pytest.ini` file configures:
- Test discovery patterns
- Async mode settings
- Test markers
- Timeout settings
- Output formatting

## Test Fixtures

### Data Generation

The test suite includes comprehensive data generators:

```python
from test_fixtures import TestDataGenerator

# Generate test data
company = TestDataGenerator.generate_company_profile()
conversation = TestDataGenerator.generate_conversation()
document = TestDataGenerator.generate_technical_document()
```

### Test Client

Simplified client creation:

```python
from test_fixtures import graphiti_test_client

async with graphiti_test_client(database="falkordb") as (session, group_id):
    # Use session for testing
    result = await session.call_tool('add_memory', {...})
```

## Performance Considerations

### LLM Latency Management

The tests account for LLM inference latency through:

1. **Configurable timeouts** - Different timeouts for different operations
2. **Mock LLM option** - Fast testing without API calls
3. **Intelligent polling** - Adaptive waiting for episode processing
4. **Batch operations** - Testing efficiency of batched requests

### Resource Management

- Memory leak detection
- Connection pool monitoring
- Resource usage tracking
- Graceful degradation testing

## CI/CD Integration

### GitHub Actions

```yaml
name: MCP Integration Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      neo4j:
        image: neo4j:5.26
        env:
          NEO4J_AUTH: neo4j/graphiti
        ports:
          - 7687:7687

    steps:
      - uses: actions/checkout@v2

      - name: Install dependencies
        run: |
          pip install uv
          uv sync --extra dev

      - name: Run smoke tests
        run: python tests/run_tests.py smoke --mock-llm

      - name: Run integration tests
        run: python tests/run_tests.py integration --database neo4j
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
```

## Troubleshooting

### Common Issues

1. **Database connection failures**
   ```bash
   # Check Neo4j
   curl http://localhost:7474

   # Check FalkorDB
   redis-cli ping
   ```

2. **API key issues**
   ```bash
   # Use mock LLM for testing without API key
   python tests/run_tests.py all --mock-llm
   ```

3. **Timeout errors**
   ```bash
   # Increase timeout for slow systems
   python tests/run_tests.py integration --timeout 600
   ```

4. **Memory issues**
   ```bash
   # Skip stress tests on low-memory systems
   python tests/run_tests.py all --skip-slow
   ```

## Test Reports

### Performance Report

After running performance tests:

```python
from test_fixtures import PerformanceBenchmark

benchmark = PerformanceBenchmark()
# ... run tests ...
print(benchmark.report())
```

### Load Test Report

Stress tests generate detailed reports:

```
LOAD TEST REPORT
================
Test Run 1:
  Total Operations: 100
  Success Rate: 95.0%
  Throughput: 12.5 ops/s
  Latency (avg/p50/p95/p99/max): 0.8/0.7/1.5/2.1/3.2s
```

## Contributing

When adding new tests:

1. Use appropriate pytest markers
2. Include docstrings explaining test purpose
3. Use fixtures for common operations
4. Consider LLM latency in test design
5. Add timeout handling for long operations
6. Include performance metrics where relevant

## License

See main project LICENSE file.

---

### Overview (`.github/actions/setup-socket-firewall/README.md`)

# Setup Socket Firewall wrapper

Composite GitHub Action that installs Socket Firewall Enterprise (`sfw`) in
wrapper mode and routes supported package-manager commands through it for later
bash steps in the same job.

## Usage

```yaml
- name: Setup Socket Firewall wrapper
  uses: ./.github/actions/setup-socket-firewall
  with:
    socket-api-key: ${{ secrets.SOCKET_API_KEY }}
```

Call the action after checkout and before the first `uv sync`, `uv lock`,
`uv pip`, or `pip install` step.

The action:

- downloads the Linux `sfw` binary when needed
- exports `SOCKET_API_KEY` and `SFW_TELEMETRY_DISABLED=true`
- configures `SFW_CUSTOM_REGISTRIES` with wrap hosts (including
  `files.pythonhosted.org` for PyPI artifact downloads)
- writes a `BASH_ENV` file with package-manager wrapper functions

Wrapper functions apply only to later bash steps that source `BASH_ENV`. They
do not affect third-party Actions or dependency installs inside Docker builds
(those use BuildKit secrets — see repository Dockerfiles).

## Fork PRs / missing secret

Graphiti is a public repository. Fork pull requests do not receive repository
secrets. When `socket-api-key` is empty, this action **soft-skips**: it prints a
notice, sets `SOCKET_FIREWALL_ENABLED=false`, and leaves package managers
unwrapped so CI still succeeds. Same-repo runs with `SOCKET_API_KEY` configured
get full enforcement.

## API key scopes

`SOCKET_API_KEY` should include the Socket scopes required for Enterprise
wrapper mode (`packages` and `entitlements:list`). Configure it as a repository
Actions secret and in the `development` / `release` environments used by CI.

## Docker builds

Official release workflows pass `socket_api_key` as a BuildKit secret so image
dependency fetches go through `sfw`. They also set the non-secret
`SOCKET_FIREWALL_ENABLED=true` build argument, which separates enforced release
layers from public fallback layers in the BuildKit cache. In enforced mode, a
missing or empty `socket_api_key` fails the build. Dockerfiles default the
argument to `false` and install directly so community `docker build` / Compose
usage continues to work without a Socket key.

### Cache bypass on enforced builds

`sfw` inspects network requests, so anything served from cache is never
scanned. Two caches would otherwise hide dependencies from an enforced release
build:

- the **BuildKit layer cache**, which skips the install step entirely when its
  inputs are unchanged
- the **`uv` cache mount**, which lets `uv` resolve wheels from disk without
  contacting PyPI even when the step does run

Enforced builds defeat both. Release workflows pass
`SOCKET_SCAN_ID=<run_id>-<run_attempt>`, which is unique per run and invalidates
the install layer, and the enforced branch exports `UV_NO_CACHE=1`. Without
these, an image whose dependencies had not changed could ship a package that
Socket flagged after the layer was first built. Community builds leave
`SOCKET_SCAN_ID` empty and keep both caches, so unauthenticated builds stay
fast.

---

### Overview (`tests/evals/data/longmemeval_data/README.md`)

The `longmemeval_oracle` dataset is an open-source dataset that we are using.
We did not create this dataset and it can be found
here: https://huggingface.co/datasets/xiaowu0162/longmemeval/blob/main/longmemeval_oracle.

---

### Mcp Server Docs Cursor Rules (`mcp_server/docs/cursor_rules.md`)

## Instructions for Using Graphiti's MCP Tools for Agent Memory

### Before Starting Any Task

- **Always search first:** Use the `search_nodes` tool to look for relevant preferences and procedures before beginning work.
- **Search for facts too:** Use the `search_facts` tool to discover relationships and factual information that may be relevant to your task.
- **Filter by entity type:** Specify `Preference`, `Procedure`, or `Requirement` in your node search to get targeted results.
- **Review all matches:** Carefully examine any preferences, procedures, or facts that match your current task.

### Always Save New or Updated Information

- **Capture requirements and preferences immediately:** When a user expresses a requirement or preference, use `add_memory` to store it right away.
  - _Best practice:_ Split very long requirements into shorter, logical chunks.
- **Be explicit if something is an update to existing knowledge.** Only add what's changed or new to the graph.
- **Document procedures clearly:** When you discover how a user wants things done, record it as a procedure.
- **Record factual relationships:** When you learn about connections between entities, store these as facts.
- **Be specific with categories:** Label preferences and procedures with clear categories for better retrieval later.

### During Your Work

- **Respect discovered preferences:** Align your work with any preferences you've found.
- **Follow procedures exactly:** If you find a procedure for your current task, follow it step by step.
- **Apply relevant facts:** Use factual information to inform your decisions and recommendations.
- **Stay consistent:** Maintain consistency with previously identified preferences, procedures, and facts.

### Best Practices

- **Search before suggesting:** Always check if there's established knowledge before making recommendations.
- **Combine node and fact searches:** For complex tasks, search both nodes and facts to build a complete picture.
- **Use `center_node_uuid`:** When exploring related information, center your search around a specific node.
- **Prioritize specific matches:** More specific information takes precedence over general information.
- **Be proactive:** If you notice patterns in user behavior, consider storing them as preferences or procedures.

**Remember:** The knowledge graph is your memory. Use it consistently to provide personalized assistance that respects the user's established preferences, procedures, and factual context.

---

--- METRICS ---
- Files Extracted: 15
- Estimated Token Budget: ~25963 tokens
- Canonical Reference: https://codewiki.google/github.com/getzep/graphiti
