Skip to main content
Complete reference for all public classes and methods exported by graphrag_sdk.

Table of Contents


GraphRAG (Facade)

The main entry point. Three primary operations: ingest(), retrieve(), and completion(). Tables go through the same ingest(); see Structured Ingestion.

Constructor

Public attributes: llm, embedder, schema, graph_store, vector_store

ingest()

Build a knowledge graph from one or more sources. Auto-detects loader from file extension. When a list of sources is provided, documents are ingested in parallel with bounded concurrency. A .csv, .tsv, .psv or .tab is read as records, not prose: its mapping is looked up in the ontology by filename (see Structured Ingestion), no model is called, and a StructuredIngestionResult is returned. A table may not appear in a list — each is written on its own — and chunker/extractor/resolver are rejected for one; identity across sources is judged by finalize(resolver=...). Pass loader=TextLoader() to read a CSV as prose anyway. Returns: IngestionResult for a single document, StructuredIngestionResult for a table, list[IngestionResult | Exception] (aligned by index) for multiple sources.

retrieve()

Retrieve context from the knowledge graph without generating an answer. Use this to inspect retrieved context or pass it to your own LLM. Returns: RetrieverResult

completion()

Full RAG pipeline: retrieve context and generate an answer. When history is provided, messages are passed natively to the LLM provider’s multi-turn chat API. Returns: RagResult Conversation history: History accepts a list of ChatMessage objects or plain dicts with role and content keys. Supported roles: "system", "user", "assistant". Invalid roles raise ValueError.
When history is provided, completion() builds a native messages list: [system_prompt, *history, user_question] and calls LLMInterface.ainvoke_messages(). Without history, it uses the single-turn ainvoke() path.

query()

Run a Cypher query against your own graph and return its rows, each a list of column values. The way to check what an ingest wrote or to aggregate over declared columns yourself. Runs exactly the Cypher you pass with no rewriting and no read-only enforcement — use params ($name) for values, never string formatting.

drop_table()

Take a table out of the graph: its Document and record chunks (and any entity only those rows mentioned), every property it signed on entities that survive, the identity it gave them (entity_key, is_stub — unless another table still keys the node), and its stored mapping, so the next ingest() of that filename is proposed a mapping afresh. The label stays in the ontology; drop_entity() removes a label nothing uses. source is matched on its basename, like ingest(). Raises: ValueError when no table of that name is in the ontology.

deduplicate_entities()

Post-ingestion entity deduplication across all documents. The LLM-judged phase is opt-in here (judge=False); finalize() runs it by default (judge=True).
  • Phase 1 (always): Canonical name match — case, accents, punctuation, inner dots (A.I. = AI), a leading English article, Surname, Given inversion, common abbreviations and one trailing legal form (Ltd, Inc) are normalised; word order is not. An acronym joins its unique same-label long form. Keeps the survivor ranked by declared key, real-over-placeholder, long form over acronym, degree, then description length; merges the duplicate’s description (" | "-joined, members kept in descriptions), aliases, source_chunk_ids and any property the survivor lacks into it; remaps RELATES and MENTIONED_IN edges; deletes duplicates.
  • Phase 2 (optional, fuzzy=True): Embedding-based — embeds entity names, finds near-duplicates by cosine similarity.
  • Phase 3 (optional, resolver=): A ResolutionStrategy judges the remaining pairs across documents and tables. finalize() passes one by default.
  • Phase 4 (opt-in here with judge=True; on by default in finalize()): LLM-judged cross-document dedup — name/description embeddings and name-in-description nominate candidate pairs, dense sets of ≤ 8 (more than half of a set’s pairs must be nominated, so a chain A–B–C–D cannot form), the judge partitions each set; with judge_vote=True (default) a second shuffled pass must agree, and pairs only one pass grouped become SAME_AS edges (agreement=1); with judge_vote=False the single pass decides and nothing is linked for disagreement — only a pair that reached across two sets is linked rather than merged, stamped with the number of passes that saw it (agreement=2 with the vote, 1 without). Agreed pairs merge by the same rules as every other phase (the survivor additionally gains every member’s label, recorded in merged_labels); pairs a resolver remembered as DISTINCT_FROM, two keyed rows of a table, and a mention two such rows could own are never asked. Agreements are unioned within one set only, so two sets sharing a member never chain into one merge. Description vectors are cached on the node (description_embedding, keyed by description_embedding_hash on the text, the embedder’s model_name and the vector’s dimension), so a later run embeds only descriptions that are new, changed, or embedded by another model; a cached vector whose dimension differs from the ones embedded now is re-embedded, with a warning. If the DISTINCT_FROM pairs cannot be read, the phase is skipped with a warning and last_judge_stats holds skipped_reason instead of counts. judge_llm defaults to the GraphRAG LLM (gpt-4.1-class recommended); given without judge=True it is ignored, with a warning. Without a resolver and with judge off — the defaults of this method — no model is called. Stats in _deduplicator.last_judge_stats.
Call once after all documents are ingested. Returns: Number of duplicate entities merged.

finalize()

Run all post-ingestion steps after all documents and tables are ingested. Bundles:
  1. deduplicate_entities(resolver=..., judge=..., judge_llm=..., judge_vote=...) — canonical-name dedup, then the resolver across sources, then the LLM-judged cross-document phase (which embeds and stores the names it reads)
  2. backfill_entity_embeddings() — name embeddings on every entity still missing one, after dedup so a duplicate about to be removed is not embedded first; entities_embedded counts this step’s vectors plus the judge’s
  3. embed_relationships() — fact text embeddings on RELATES edges
  4. ensure_indices() — all indexes
  5. A consistency report over the whole graph
Returns: FinalizeResult — counts entities_deduplicated, entities_linked (judge SAME_AS edges), judge_llm_calls, judge_stats, entities_embedded, relationships_embedded, null_stubs_removed, indexes, and the report: resolved_duplicates, rejected_duplicates, probable_duplicates (spellings the canonical rule declined to merge, with the reason), unmerged_name_collisions (one name under two declared labels), property_conflicts (a property two tables both supply, with how many entities hold differing values; every value is kept, none resolved for you), unresolved_references (placeholders whose owning table has not arrived), entities_without_a_name, proposed_mappings (tables loaded on a proposed, not declared, mapping), mapping_changed, stale_signed_properties. Everything non-empty is also logged at WARNING.

Sync Wrappers

Convenience methods that run the async versions in asyncio.run().

Connection

ConnectionConfig

FalkorDBConnection


Providers

LLMInterface (ABC)

ainvoke_messages() is used by completion() when conversation history is provided. The default implementation concatenates messages into a single prompt string and calls ainvoke(), so custom providers work without changes. LiteLLM and OpenRouterLLM override this with native multi-turn implementations.

Embedder (ABC)

LLMBatchItem

LiteLLM

LiteLLMEmbedder

OpenRouterLLM

OpenRouterEmbedder


Data Models

All models extend DataModel (Pydantic BaseModel with extra="allow").

GraphNode

GraphRelationship

GraphData

TextChunk

TextChunks

DocumentInfo

DocumentOutput

IngestionResult

RagResult

RetrieverResult

RetrieverResultItem

ResolutionResult

ChatMessage

Validated message type for multi-turn conversations. Used by completion(history=...) and LLMInterface.ainvoke_messages(). Invalid roles raise a validation error on construction. LLMMessage is a backward-compatible alias for ChatMessage.

LLMResponse

SearchType

Extraction Models

compute_entity_id()

Deterministic entity ID from normalized name and optional type. When entity_type is provided, appends a __type suffix to prevent cross-type collisions (e.g. paris__person vs paris__location). Without entity_type, returns just the normalized name for backwards compatibility.

Schema

EntityType

RelationType

PropertyType

GraphSchema


Ingestion Strategies

LoaderStrategy (ABC)

Built-in: TextLoader(encoding="utf-8"), PdfLoader()

ChunkingStrategy (ABC)

Built-in: FixedSizeChunking(chunk_size=1000, chunk_overlap=100)

ExtractionStrategy (ABC)

Built-in:
  • GraphExtraction(llm, *, entity_extractor=None, coref_resolver=None, entity_types=None, relation_types=None, max_concurrency=None) — entity_types=None selects DEFAULT_ENTITY_TYPES; relation_types=None selects DEFAULT_RELATION_TYPES, a 31-label UPPER_SNAKE_CASE vocabulary (LOCATED_IN, PART_OF, EMPLOYED_AT, AUTHORED, …) the step-2 prompt asks the LLM to prefer; off-list labels are still kept. Pass relation_types=[] for open-vocabulary labels. Both are overridden by a declared Ontology, whose relations are enforced.
Entity Extractors (step 1 backends for GraphExtraction):
  • GLiNERExtractor(threshold=None, model_name=None, window_tokens=None, window_overlap=48, candidate_threshold=<25 % below threshold>) — default, local NER. model_name=None selects urchade/gliner_medium-v2.1; threshold=None selects the measured per-model value (0.75 for that model). Spans scoring between candidate_threshold and threshold are returned as "Unknown"; pass candidate_threshold=None to disable. Chunks longer than window_tokens (derived from the model’s max_len when None) are processed as overlapping windows.
  • LLMExtractor(llm, threshold=0.75) — LLM-based NER
  • Subclass EntityExtractor for custom backends

ResolutionStrategy (ABC)

Built-in:
  • ExactMatchResolution(llm=None, resolve_property="name", cross_label_merge=True) — ingest()’s default is ExactMatchResolution(llm=None, cross_label_merge=False)
  • LLMVerifiedResolution(llm=None, embedder=None, *, hard_threshold=0.95, soft_threshold=0.65, max_llm_pairs=500, max_llm_concurrency=None, force_summary_threshold=3, max_summary_tokens=500, ann_top_k=50, batch_verification=True, verification_token_budget=4000, verification_max_pairs_per_call=5, cross_label_merge=True, cross_label_vector_door=0.60, cross_label_rank_floor=0.55, cross_label_max_pairs=200, cross_label_min_descriptions=3, unified_stage=True, unified_threshold=0.65, label_family_gate=True, cross_label_vote=True) — opt-in at ingest(); finalize()’s default cross-source resolver

Ingestion Pipeline


Structured Ingestion

A table is declared on the ontology, next to the entity types, and then ingested through the ordinary ingest(). No model is involved in writing it: identity comes from a declared key, every property type is declared, and the same file always produces the same graph. A row and a prose mention of the same thing share one node from the first write. The full guide is Structured Ingestion; the runnable version is examples/11_structured_ingestion.py.

TableMapping

MappingError is raised for a declaration that does not fit — an unknown column, a cell that does not parse as its type, a label the sanitiser would rewrite — and the graph is left untouched.

Column

COLUMN_TYPES: STRING, INTEGER, FLOAT, BOOLEAN, DATE, LIST. Types are declared, never sniffed. INTEGER and FLOAT accept 1,234.56 and 1.234,56; FLOAT refuses nan/inf; LIST is parsed as a CSV row. A blank cell writes no property.
Written as a RELATES edge with rel_type=type, like every data edge. The target is keyed, never named: a missing target is created as a placeholder (is_stub: true) that is renamed in place — edges intact — when the owning table arrives; an existing target, another table’s row or an entity a document mentioned, only gains the key. A link can therefore never overwrite what the owning source wrote.

Record loaders

CsvRecordLoader is the default; delimiter=None sniffs comma, semicolon, tab or pipe from the first kilobyte, and the default encoding reads UTF-8 with or without the byte-order mark Excel writes. The file is read once before anything is written: a row with more fields than the header — an unquoted delimiter inside a cell — is refused with its row number rather than loaded shifted. Implement RecordLoaderStrategy for another format and pass it as ingest(..., record_loader=...).

StructuredIngestionResult

Returned by ingest() for a table (graphrag_sdk.ingestion.structured_pipeline.StructuredIngestionResult).

How a table is written

  • Re-ingesting is a re-sync. A table is a snapshot: a deleted row is removed, a blanked cell loses its column, a moved foreign key leaves its old target, and a node another source still mentions survives with the table’s signed columns and identity retracted. The content hash covers the mapping, so re-declaring a type rewrites the source.
  • A Document records how it was written, so update() cannot re-read a table as prose or a document as records. update(), delete_document() and drop_table() all take the table’s name.
  • No mapping declared? With a model, one is proposed from the measured columns, the first rows and the current ontology, held to the data (unique key, existing columns, known link targets), stored as derived=True and reported by finalize().proposed_mappings. Without a model the file is read as-is: the label from the filename, the leftmost unique-and-complete column as key, no name column. Declare a TableMapping to replace it, or drop_table() to refuse it.
  • Ontology registration is additive. Declared types reach the ontology so text-to-Cypher sees age as an INTEGER; an existing label is extended, not redeclared, and a type contradiction raises.

Retrieval Strategies

RetrievalStrategy (ABC)

Uses the Template Method pattern.
set_ontology() is called by the facade whenever the working ontology changes — an evolution call, a table declaring typed columns — so a strategy that generates Cypher always sees the current schema. A no-op by default; MultiPathRetrieval overrides it.

LocalRetrieval

MultiPathRetrieval


Reranking Strategies

RerankingStrategy (ABC)

CosineReranker


Storage

GraphStore

VectorStore


Context

Execution context for logging and budget tracking.

Exceptions