Skip to main content
This document is the comprehensive configuration reference for GraphRAG SDK v2. Each section covers a configurable component, its parameters, defaults, and usage examples.

1. ConnectionConfig

ConnectionConfig is a dataclass that defines how the SDK connects to a FalkorDB instance. It is passed to GraphRAG or used to create a FalkorDBConnection directly.

Fields

Creating from a URL

ConnectionConfig.from_url() parses a redis:// URL and returns a ConnectionConfig:
The URL format is redis://[user:pass@]host[:port][/db]. Any keyword argument overrides the value parsed from the URL.

Passing to GraphRAG

You can pass either a ConnectionConfig or a pre-built FalkorDBConnection:

Retry Behavior

Each query is attempted up to retry_count times in total, the first try included, with exponential backoff and jitter between attempts (retry_delay * 2^attempt * random(0.5, 1.5), attempt counting from 0). Everything that is not a server reply is retried: a lost connection or socket timeout, LOADING, an error raised in the driver. Of the server’s replies only the temporary ones are: a full query queue, BUSY, NOREPLICAS, MISCONF, a cluster redirect or failover, a read-only replica, and out of memory. If one outlasts every attempt, the circuit breaker is open, or the first connection to the server fails, query() raises DatabaseUnavailableError. On a server that accepts connections but never answers, that takes about retry_count × (query_timeout_ms + 5 s); lower either to fail sooner. Any other server reply means the query itself was rejected — a syntax error, an unknown variable, division by zero, a MERGE on a null key, a missing index, a procedure called with the wrong arguments. A read the server stops at its timeout (query_timeout_ms, or the timeout= passed to query()) is treated the same way, since a retry would cost as much again. FalkorDB applies no timeout to writes: a write slower than query_timeout_ms + 5 s is cut on the client as a socket timeout and re-sent, while the first attempt may still run. Both raise DatabaseError on the first attempt, and the circuit breaker counts them as a healthy answer, not a failure. DatabaseUnavailableError subclasses DatabaseError, so except DatabaseError catches both.

2. LLM Providers

The SDK defines an abstract LLMInterface base class. All LLM providers must implement invoke() for synchronous calls. Async calls (ainvoke) default to running invoke in a thread pool but can be overridden for true async support.

Common Parameters

The LLMInterface base class accepts: LiteLLM supports 100+ LLM providers through a unified interface. Install with pip install graphrag-sdk[litellm].
Parameters:

OpenRouter

OpenRouter provides access to many models through a single API. Install with pip install graphrag-sdk[openrouter].
Parameters:

Azure OpenAI via Environment Variables

When using LiteLLM with Azure, the following environment variables are recognized:
Then configure the LLM:

Custom LLM Provider

Implement the LLMInterface abstract class:

3. Embedder Providers

The SDK defines an abstract Embedder base class with embed_query() (single text) and embed_documents() (batch). Batch embedding is critical for performance.

Performance Note: Batch Embedding

Individual embedding calls to Azure OpenAI take approximately 0.22 seconds each. A batch of 500 texts takes approximately 8 seconds. Always use batch embedding (embed_documents / aembed_documents) rather than looping over embed_query.

LiteLLMEmbedder

Supports OpenAI, Azure, Cohere, and other embedding models via LiteLLM.
Parameters: For Azure OpenAI, set batch_size=500 to stay within the API rate limits. The default of 2048 works well for OpenAI’s direct API.

OpenRouterEmbedder

Parameters:

Custom Embedder

Implement the Embedder abstract class:
Override aembed_query and aembed_documents if your provider supports true async. The defaults run the sync methods in a thread pool via asyncio.to_thread.

Binary-Split Error Recovery

Both LiteLLMEmbedder and OpenRouterEmbedder implement binary-split error recovery for batch embedding. If a batch fails with a transient error, the batch is split in half and each half is retried recursively. Non-transient errors (401, 403, authentication failures) are raised immediately.

4. GraphSchema

GraphSchema defines the structure of your knowledge graph. It constrains LLM extraction and powers the pruning step that filters non-conforming data.

Components

EntityType — defines a node type: RelationType — defines a relationship type: PropertyType — defines a property on a node or relationship:

Example Schema Definition

Each RelationType.patterns entry is a (source_label, target_label) tuple. An empty patterns list means the relation is allowed between any entity types.

Open Schema Mode

If no entity types or relation types are defined (empty GraphSchema()), the extraction operates in open-schema mode and the pruning step is skipped. This lets the LLM extract any entities and relationships it finds.

5. Pipeline Tuning

Chunking Parameters

FixedSizeChunking splits text into fixed-size character windows with overlap.
Larger chunks provide more context per extraction call but increase LLM token usage. The benchmark-optimized values (1500/200) balance extraction quality against cost.

Extraction Strategy Parameters

GraphExtraction — composable 2-step extraction (GLiNER NER + LLM relationship extraction): Built-in entity extractors:

Custom Entity Types

Override the default 11 entity types with your own domain-specific ontology:
Priority: schema.entities > entity_types param > defaults (Person, Organization, Technology, Product, Location, Date, Event, Concept, Law, Dataset, Method).

LLM Concurrency

The LLMInterface.max_concurrency parameter (default: 12) controls how many LLM calls run in parallel during abatch_invoke(). Set it lower to avoid rate limits:
For GraphExtraction, you can also pass max_concurrency directly:

6. Retrieval Tuning

MultiPathRetrieval

MultiPathRetrieval is the default retrieval strategy. It combines multiple search paths with cosine reranking.

Retrieval Pipeline (9 Steps)

The retrieval pipeline proceeds as follows:
  1. Keyword extraction — stopword filtering + LLM proper-noun extraction.
  2. Embed question — single embedding API call for the query.
  3. RELATES edge vector search — finds fact strings and entity entry points via edge embeddings.
  4. Entity discovery (2 paths) — Cypher CONTAINS on entity names + fulltext search on the __Entity__ index. Merged with entities from step 3.
  5. Relationship expansion — 1-hop (top 15 entities, limit 150) + 2-hop (top 5 entities, limit 25) traversal of RELATES edges.
  6. Chunk retrieval (4 paths) — fulltext search, vector search, MENTIONED_IN traversal, and 2-hop entity-to-neighbor-to-chunk traversal.
  7. Source document names — batch-fetch document paths via PART_OF edges.
  8. Cosine reranking — batch-embed candidate chunks and sort by cosine similarity to the query vector.
  9. Context assembly — structured sections: hint, entities, relationships, facts, passages.

Overriding the Default Strategy

Pass a custom strategy to individual queries or set it as the default:

7. Post-Ingestion

After all documents have been ingested, run post-ingestion steps to deduplicate entities, embed entity names and relationships, and ensure all indexes exist.

finalize() — All-In-One

The recommended approach is to call finalize() after all ingestion is complete. It bundles four steps in order:
  1. Remove NULL-name stub entities left by legacy writes (only nodes no mapping wrote).
  2. deduplicate_entities() — incremental and crash-safe: the entities whose check is pending (added since the last finalize(), or left pending by it) get exact-name dedup, then one judge pass over their nearest names in the whole graph. Dedup also gives every entity its name embedding (its id, for an entity with no name, which is never judged). A second finalize() with nothing pending does no dedup work. An entity whose check did not complete stays pending and is retried; after 3 judged runs without a usable verdict it is reported in dedup_unresolved and no longer retried. An interrupted finalize() is finished by the next one. finalize(dedup_all=True) checks every entity.
  3. embed_relationships() — embed fact text on RELATES edges.
  4. ensure_indices() — create all 5 standard indexes (idempotent).
GraphRAG SDK requires FalkorDB 4.20 or later (see Storage). One dedup run per graph at a time: a second finalize() or deduplicate_entities() — from another process, or on the same GraphRAG — raises DedupRunActiveError when it reaches dedup, and the running one is left untouched. update() and deletes may run meanwhile.
A synchronous convenience method is also available:

deduplicate_entities() — Entity Deduplication

Call this when you need fine-grained control over deduplication.
Parameters: Only the pending entities are checked: those with no name vector yet (ingest writes entities without one), and those a pending system record lists — a run lists every entity before it embeds it, and clears it once its check completes. An entity whose check did not complete (a failed lookup or judge call, an answer that was not a clean partition, a group held for one, a merge an update() changed) stays pending for the next run. See Storage for the merge plan and crash safety. Phase 1 (always runs): Exact name match. Groups the pending entities and their nearest names from the vector index by canonical name (case, accents, punctuation and a leading English article folded; A.I. = AI; Surname, Given un-inverted; common abbreviations expanded; one trailing legal form such as Ltd or Inc dropped) and label to prevent cross-type merging. An acronym joins its long form when exactly one same-label entity spells its initials (AIHS / Ashford Island Historical Society). The survivor is the node whose id came from a declared table key, then a real node over a placeholder, then the long form over an acronym, then the best-connected entity (most RELATES and MENTIONED_IN edges), then the longest description, then the longest name. A group of extracted mentions only goes to the judge instead: the same spelling is evidence, not proof. The duplicate’s description is appended to the survivor’s with " | ", its name is recorded in the survivor’s aliases, its source_chunk_ids are unioned in, any property the survivor lacks is copied over, and every relationship it has, of every type, moves to the survivor in the same query that deletes it. Phase 2: judged match. Each pending entity’s nearest names from the vector index form small candidate sets; the judge partitions each set once, and agreed groups merge by the Phase 1 rules. Every set is judged before anything merges. See Storage.

backfill_entity_embeddings() — Entity Vector Backfill

Embeds __Entity__ nodes that are missing embeddings. Queries entities where embedding IS NULL, batch-embeds the entity name, and stores vectors. It is not a dedup step: an entity with a vector and no pending record counts as checked, so a vector written here skips that entity’s dedup check. finalize() no longer calls it — dedup embeds every entity — and after calling it directly, run finalize(dedup_all=True) once.

embed_relationships() — RELATES Edge Embeddings

Batch-embeds all RELATES edges that have a fact property but are missing embeddings. These edge embeddings power the RELATES vector search path in retrieval.

ensure_indices() — Index Creation

Creates all standard indexes (idempotent — safe to call repeatedly):
Note: ensure_indices() is called automatically after each ingest() call. The finalize() method resets the internal _indices_ensured flag and re-runs it to catch any newly needed indexes. It returns once FalkorDB reports every index built (up to 10 minutes), since a vector search on an index still being built finds nothing.

When to Call Each

Do not call backfill_entity_embeddings() between ingests: every entity it embeds skips its dedup check. Ingest all documents first, then call finalize() once.