DeepFellow DOCS

Knowledge Graph

Ingest files into a per-project FalkorDB knowledge graph, then search it or ask it questions in natural language.

Knowledge Graph adds a knowledge graph to DeepFellow Server, backed by GraphRAG-SDK and FalkorDB. A vector store finds text chunks similar to a query. The knowledge graph instead extracts the entities in your documents, such as people, organizations, and products, together with the relationships between them, and answers questions by following those relationships across documents.

Each project gets its own graph, named after the project ID, so ingested documents and searches from one project never leak into another.

How It Works

Ingesting a file into the knowledge graph runs the following pipeline:

  1. Chunking. Doc Chunker converts the uploaded file and splits it into heading-aware chunks. A file that Doc Chunker has already converted, for example for a vector store, reuses the cached result.
  2. Grouping. The server merges consecutive chunks into graph documents. See How Chunks Become Graph Documents.
  3. Entity extraction. A GLiNER model, hosted on DeepFellow Infra as the gliner custom service, finds entities in each document. GLiNER only looks for the entity types declared in the project's ontology.
  4. Relation extraction. The LLM set in graph.llm_model verifies the entities and extracts the relations between them. Relations whose source and target entity types match none of the relation's patterns in the ontology are dropped and reported in the job's warnings.
  5. Finalization. Once every document of the file is ingested, the server deduplicates entities with identical names and builds the embeddings and indexes that search uses. See Entity Deduplication.

Ingestion runs in the background on Nightshift, so the ingest request returns immediately with a job ID.

A job ingests up to graph.document_ingestion_concurrency of its documents at once. Documents that are not in the graph yet ingest in parallel. Replacing a document that is already in the graph, removing documents, and finalization run one at a time, and no other write to the project's graph runs alongside them.

At query time, POST /v1/graph/search and POST /v1/graph/completion use GraphRAG-SDK's multi-path retrieval. It extracts keywords from the question, finds matching entities and relations through vector and full-text search, expands the relations of the top entities by up to two hops, and reranks the source passages it collected. POST /v1/graph/completion then hands the retrieved context to graph.llm_model to write an answer.

Prerequisites

Knowledge Graph depends on the following services:

ServiceRoleWhere to Get It
FalkorDBStores the graphs.Installed by deepfellow server install --falkordb-active, or any external FalkorDB instance.
Doc ChunkerConverts and chunks files before ingestion and ontology discovery.Custom service on DeepFellow Infra. See Doc Chunker.
GLiNERExtracts entities.gliner custom service on DeepFellow Infra. See Custom Service.
LLMExtracts relations, answers questions, and drafts ontologies.Any chat model installed on DeepFellow Infra.
Embedding modelEmbeds entities and passages for search.Any embedding model installed on DeepFellow Infra.

The GLiNER custom service offers a CPU and a GPU image. The install form picks the GPU image when you assign an NVIDIA GPU to the service, and the CPU image otherwise. Keep the service's prefix at its default, gliner, or set graph.entity_extractor_prefix to the prefix you chose. The service serves the urchade/gliner_medium-v2.1 model.

Enable Knowledge Graph

Knowledge Graph is disabled by default.

Install the server with a FalkorDB instance.

To run a FalkorDB container managed by DeepFellow, install (or reinstall) the server with --falkordb-active:

deepfellow server install --falkordb-active

The installer adds a falkordb container to the stack, generates its password, and writes DF_GRAPH__ENABLED=true, DF_GRAPH__HOST, DF_GRAPH__PORT, and DF_GRAPH__PASSWORD to the server's .env. To connect to an existing FalkorDB instance instead, answer no when the installer offers a local FalkorDB, or pass --falkordb-url HOST:PORT together with --falkordb-password (and --falkordb-username if your instance requires one). For every install flag, see deepfellow server Commands.

Install the gliner and doc_chunker custom services on DeepFellow Infra, together with the chat and embedding models the graph will use.

Start the server:

deepfellow server start

Point the graph at your models.

graph is a dynamic settings section, so the change applies immediately, without a restart. Pass the fields to change as a JSON object on the graph section:

deepfellow server config set graph='{
  "enabled": true,
  "llm_model": "gemma3:12b",
  "ontology_discovery_llm_model": "gemma3:12b",
  "embedding_model": "mxbai-embed-large",
  "embedding_dim": 1024
}'

The server merges the object into the current graph section, so any field you leave out, such as host or password, keeps its current value. The Knowledge Graph (FalkorDB) section of the Server Configuration card edits the same connection, model, grouping, and deduplication fields. See Setting a Nested Field.

Confirm the connection.

Run deepfellow server logs -f and look for Knowledge graph enabled (FalkorDB: HOST:PORT). While graph.enabled is false, the server logs Knowledge graph is not configured. instead and runs normally without FalkorDB.

Pick graph.embedding_model and graph.embedding_dim before you ingest anything. graph.embedding_dim has to match the vector size the embedding model returns, for example 1024 for mxbai-embed-large. Embeddings already stored in a graph stay as they are when you change either value, so a graph ingested with one embedding model does not match queries embedded with another.

Turning graph.enabled on or off through deepfellow server config set, the Server Configuration card, or PUT /admin/config takes effect immediately for every graph endpoint, including ingestion and ontology discovery. The Nightshift worker picks up the change on its own as well. See Configuration Reload.

Settings

Every setting below is a dynamic setting. It seeds from its DF_GRAPH__* environment variable on first boot and lives in config.json afterward.

SettingEnvironment VariableDescriptionDefault
graph.enabledDF_GRAPH__ENABLEDEnables or disables Knowledge Graph.false
graph.hostDF_GRAPH__HOSTHostname of the FalkorDB instance.localhost
graph.portDF_GRAPH__PORTPort of the FalkorDB instance.6379
graph.usernameDF_GRAPH__USERNAMEFalkorDB username. Leave it empty for a local instance, which has no users by default.empty
graph.passwordDF_GRAPH__PASSWORDFalkorDB password. Masked in deepfellow server config get unless you pass --secret.empty
graph.sslDF_GRAPH__SSLConnects to FalkorDB over TLS, for example to a hosted instance.false
graph.llm_modelDF_GRAPH__LLM_MODELModel on DeepFellow Infra used for relation extraction, retrieval, and answers.gpt-4o-mini
graph.embedding_modelDF_GRAPH__EMBEDDING_MODELEmbedding model on DeepFellow Infra.text-embedding-3-small
graph.embedding_dimDF_GRAPH__EMBEDDING_DIMVector size graph.embedding_model returns.1536
graph.entity_extractor_prefixDF_GRAPH__ENTITY_EXTRACTOR_PREFIXEndpoint prefix of the GLiNER custom service on DeepFellow Infra.gliner
graph.entity_extractor_timeout_secondsDF_GRAPH__ENTITY_EXTRACTOR_TIMEOUT_SECONDSSeconds the server waits for one response from the GLiNER custom service.30
graph.entity_extractor_thresholdDF_GRAPH__ENTITY_EXTRACTOR_THRESHOLDGLiNER confidence, from 0 to 1, below which an entity gets the Unknown type instead of the type GLiNER found.0.75
graph.entity_extractor_max_retriesDF_GRAPH__ENTITY_EXTRACTOR_MAX_RETRIESRetries of a GLiNER call that timed out, failed to connect, or got a 5xx response. See Retries and Failures.2
graph.document_min_charsDF_GRAPH__DOCUMENT_MIN_CHARSSize a group of chunks has to reach before a heading change starts a new graph document.1000
graph.document_max_charsDF_GRAPH__DOCUMENT_MAX_CHARSSize at which a group of chunks becomes a graph document, even without a heading change.8000
graph.fuzzy_dedup_enabledDF_GRAPH__FUZZY_DEDUP_ENABLEDMerges entities with similar, not only identical, names after each ingestion.false
graph.fuzzy_dedup_thresholdDF_GRAPH__FUZZY_DEDUP_THRESHOLDCosine similarity, from 0 to 1, two entity names need for a fuzzy merge.0.95
graph.document_ingestion_concurrencyDF_GRAPH__DOCUMENT_INGESTION_CONCURRENCYMaximum number of documents of one ingestion job that ingest at once. Minimum 1. See How It Works.4
graph.document_ingestion_max_attemptsDF_GRAPH__DOCUMENT_INGESTION_MAX_ATTEMPTSNightshift deliveries an ingestion job gets before it is marked failed. See Retries and Failures.3
graph.ingestion_stale_after_secondsDF_GRAPH__INGESTION_STALE_AFTER_SECONDSSeconds without an update after which an in_progress ingestion job counts as abandoned, and an ingest request resumes it. A running job updates itself periodically, so a job only counts as abandoned once its worker stops, for example after a server restart. See Ingest the Same File Again.7200
graph.ontology_discovery_llm_modelDF_GRAPH__ONTOLOGY_DISCOVERY_LLM_MODELModel on DeepFellow Infra used to draft ontologies.gpt-4o-mini
graph.ontology_discovery_sample_chunks_per_docDF_GRAPH__ONTOLOGY_DISCOVERY_SAMPLE_CHUNKS_PER_DOCNumber of chunks ontology discovery reads from each sample file.3
graph.ontology_discovery_concurrencyDF_GRAPH__ONTOLOGY_DISCOVERY_CONCURRENCYNumber of LLM calls ontology discovery runs in parallel.4
graph.ontology_discovery_max_sample_filesDF_GRAPH__ONTOLOGY_DISCOVERY_MAX_SAMPLE_FILESMaximum number of sample files per discovery. Values above 10 have no effect.10
graph.ontology_discovery_max_attemptsDF_GRAPH__ONTOLOGY_DISCOVERY_MAX_ATTEMPTSNightshift deliveries an ontology discovery gets before it is marked failed.3
graph.ontology_discovery_llm_call_attemptsDF_GRAPH__ONTOLOGY_DISCOVERY_LLM_CALL_ATTEMPTSAttempts, at least 1, each ontology discovery LLM call gets when the model returns a rate-limit or server error.4
graph.ontology_discovery_max_new_entitiesDF_GRAPH__ONTOLOGY_DISCOVERY_MAX_NEW_ENTITIESMaximum number of entity types, not already in the seed ontology, one discovery adds. 0 means no cap.0
graph.ontology_discovery_max_new_relationsDF_GRAPH__ONTOLOGY_DISCOVERY_MAX_NEW_RELATIONSMaximum number of relation types, not already in the seed ontology, one discovery adds. 0 means no cap.0

The defaults of graph.llm_model, graph.embedding_model, graph.embedding_dim, and graph.ontology_discovery_llm_model name OpenAI models. Replace them with models installed on your DeepFellow Infra instance.

The ingestion settings, graph.document_ingestion_concurrency and the graph.entity_extractor_* settings, apply to the next ingestion job or document retry. Every other graph setting applies to the next request.

The ontology discovery settings are described in Knowledge Graph Ontology.

Use the Web UI

The project details page has two buttons in its Toolboxes section header:

  • Knowledge Graph lists the project's files with their graph ingestion status. Upload a file, ingest it, expand a file row to see its graph documents, ingest or retry a single document, re-ingest a file, or remove a file from the graph.
  • Ontology drafts, edits, and approves the project's ontology. See Knowledge Graph Ontology.

The Knowledge Graph column mirrors the file's graph_ingestion_status: Not ingested for null, Converted for pending, Processing… for in_progress, Ingested for completed, and Failed for failed. The trash button is disabled for files that are not ingested or still ingesting.

Ingest a File

Upload a file with POST /v1/files (see Upload Files), then queue it for ingestion into the knowledge graph:

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/files/FILE_ID/ingest' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/graph/files/FILE_ID/ingest",
    headers={"Authorization": "Bearer DEEPFELLOW_PROJECT_API_KEY"},
)

print(response.json())
const response = await fetch('https://deepfellow-server-host/v1/graph/files/FILE_ID/ingest', {
    method: 'POST',
    headers: {
        Authorization: 'Bearer DEEPFELLOW_PROJECT_API_KEY'
    }
});

const data = await response.json();
console.log(data);

The endpoint returns 202 Accepted:

{
    "status": "queued",
    "document_count": 0,
    "job_id": "68da445c5186deb8bca2bde9"
}

document_count is 0 for a new job because chunking happens in the background. To see the documents once chunking finishes, check the job's status.

Ingestion uses the project's active ontology at the time the ingestion job starts. An ontology you approve while a job runs applies to the next job. To tailor extraction to your domain, discover and approve an ontology before you ingest your files.

Ingest the Same File Again

What a second ingest request for the same file does depends on the file's latest job:

Latest Job StatusWithout forceWith ?force=true
NoneStarts a new job.Starts a new job.
in_progressReturns 409 Conflict, unless the job has had no update for graph.ingestion_stale_after_seconds. Such a job is treated as abandoned, for example after a server restart, and resumed.Marks the running job failed and starts a new job.
completedReturns "status": "completed" with that job's ID and document count. Nothing is queued.Starts a new job that chunks the file again and updates its documents in place. See Forced Re-ingest.
pending or failed, with documentsResumes the same job. Documents already completed or removed are skipped, and the file is not chunked again.Starts a new job that chunks the file again and updates its documents in place. See Forced Re-ingest.
pending or failed, without documentsResumes the same job and chunks the file again.Starts a new job that chunks the file again.

A job has no documents when it failed before chunking finished, for example because Doc Chunker was unreachable. Calling the ingest endpoint again chunks the file and continues.

Ingesting a document that is already in the graph replaces its earlier version instead of adding to it, so retries, resumed jobs, and repeated ingests never duplicate a document's chunks or entities.

Forced Re-ingest

A forced re-ingest does not remove anything from the graph when you call it. Once the new job has chunked the file, the server compares its documents with the ones from the file's earlier jobs:

  • A document the file no longer has, because the file now splits into fewer documents, is removed from the graph.
  • A document that never finished ingesting in an earlier job is removed and ingested from scratch.
  • A document extracted with a different ontology than the project's current one is removed and ingested from scratch.
  • Any other document is updated in place. If its text is unchanged, it is skipped without new extraction.

Use ?force=true after you change the project's ontology, graph.document_min_chars, or graph.document_max_chars, to rebuild a file's part of the graph with the new settings. After an ontology change, every document of the file is extracted again. After a change to graph.document_min_chars or graph.document_max_chars, only the documents whose text changed are. This is what Re-ingest does in the web UI.

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/files/FILE_ID/ingest?force=true' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

How Chunks Become Graph Documents

The server groups Doc Chunker's chunks into graph documents before ingestion. Graph documents large enough to carry context produce better relations than single paragraphs, and documents small enough to fit in one LLM response keep extraction from being cut off.

  • Consecutive chunks under the same heading path merge into one graph document.
  • A heading change starts a new graph document only once the current group has reached graph.document_min_chars. Below that size, the group keeps growing past the heading change, and the new heading is kept inline as a [Heading > Subheading] marker.
  • A group becomes its own graph document as soon as it reaches graph.document_max_chars, even without a heading change.
  • A table chunk always becomes its own graph document, with source set to json, regardless of the size of the surrounding text.

Each graph document starts with a Section: HEADING_PATH line. Its document_id is FILE_ID:INDEX, and its source_description names the file, the chunk range, and the Doc Chunker references it came from, for example notes.pdf, chunks 0..3, refs #/texts/0..11.

Check Ingestion Status

Poll a job by the ID the ingest request returned:

curl -X 'GET' \
  'https://deepfellow-server-host/v1/graph/ingestion_jobs/JOB_ID' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

To fetch a file's most recent job without knowing its ID, call GET /v1/graph/files/{file_id}/ingestion_job. It returns 404 Not Found if the file was never ingested.

Response:

{
    "id": "68da445c5186deb8bca2bde9",
    "file_id": "68da445c5186deb8bca2bdea",
    "status": "completed",
    "document_count": 2,
    "document_counts": {
        "pending": 0,
        "in_progress": 0,
        "completed": 2,
        "failed": 0,
        "removed": 0
    },
    "documents": [
        {
            "index": 0,
            "name": "introduction-000",
            "content": "Section: Introduction\n\nAcme Corp hired Alice Smith as its CTO in March 2026...",
            "source": "text",
            "source_description": "notes.pdf, chunks 0..3, refs #/texts/0..11",
            "status": "completed",
            "document_id": "68da445c5186deb8bca2bdea:0",
            "error": null,
            "warnings": []
        },
        {
            "index": 1,
            "name": "pricing-004-table",
            "content": "Section: Pricing\n\n| Plan | Price |\n| --- | --- |\n| Basic | $10 |",
            "source": "json",
            "source_description": "notes.pdf, chunk 4 (table), refs #/tables/0",
            "status": "completed",
            "document_id": "68da445c5186deb8bca2bdea:1",
            "error": null,
            "warnings": []
        }
    ],
    "last_error": null,
    "created_at": "2026-08-03T10:00:00Z",
    "updated_at": "2026-08-03T10:05:00Z"
}
FieldDescription
statusJob status: pending, in_progress, completed, or failed. A job is completed once every document is completed or removed, and failed once every document has finished and at least one of them failed.
document_countNumber of graph documents the file was split into. 0 until chunking finishes.
document_countsNumber of the job's documents in each status: pending, in_progress, completed, failed, and removed. Use it to show the job's progress without counting documents yourself.
documents[].statusEach document's own status: pending, in_progress, completed, failed, or removed. A failed document does not stop the rest of the job. A removed document was deleted from the graph with DELETE /v1/graph/documents/{document_id}, and counts as finished without failure.
documents[].contentFull text of the document as it was ingested.
documents[].errorError message of a failed document, otherwise null.
documents[].warningsRelations the extractor dropped because their source and target entity types did not match any pattern in the ontology. Empty when there were none. A long list here is a hint that the ontology is missing a relation pattern.
last_errorError that failed the whole job, such as a chunking or finalization failure. null otherwise.

The file object returned by GET /v1/files/{file_id} also carries graph_ingestion_status and graph_ingestion_job_id. graph_ingestion_status is null for a file that is not in the graph.

Retry a Single Document

To ingest a pending document, retry a failed one, or add a removed one back to the graph, without touching the rest of the job:

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/ingestion_jobs/JOB_ID/documents/INDEX/retry' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

The endpoint returns 202 Accepted with {"status": "queued", "job_id": "JOB_ID", "index": INDEX} and sets the job back to in_progress until the document finishes. It returns 404 Not Found for an index outside the job. It returns 409 Conflict, without changing the document, in the following cases:

  • The document is in_progress or completed.
  • The job is not the file's latest job, because a newer ingestion of the file replaced it.
  • The job is not running and still has other pending documents. To ingest all of them, call POST /v1/graph/files/{file_id}/ingest instead.
  • Another ingestion of the file is in progress.

A retried document is always ingested from scratch.

Retries and Failures

A document fails on its own when entity extraction, relation extraction, or the FalkorDB write for that document fails. The job moves on to the next document, and the failed document stays failed until you retry it.

Before a document fails on a GLiNER call, the server retries the call up to graph.entity_extractor_max_retries times, waiting 0.5 seconds before the first retry and twice as long before each next one. Only timeouts, connection errors, and 5xx responses get a retry. A 4xx response, or a response the server cannot read, fails the document at once.

A whole job fails, with last_error set, when a step shared by all its documents fails: reading the file, chunking it, or the finalization step after the last document. A job that fails before finalization is redelivered with backoff until it reaches graph.document_ingestion_max_attempts deliveries. A finalization failure fails the job at once. In both cases, calling the ingest endpoint again resumes the job.

Search the Graph

POST /v1/graph/search takes a natural language query and returns the context the graph retrieves for it, without generating an answer:

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/search' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "Who is the CTO of Acme Corp?"
}'

Response:

{
    "results": [
        {
            "content": "## Key Entities\n- Alice Smith\n- Acme Corp",
            "score": null,
            "metadata": { "section": "entities" }
        },
        {
            "content": "## Entity Relationships\n- Alice Smith WORKS_FOR Acme Corp",
            "score": null,
            "metadata": { "section": "relationships" }
        },
        {
            "content": "## Source Document Passages\nAcme Corp hired Alice Smith as its CTO in March 2026.",
            "score": null,
            "metadata": { "section": "passages" }
        }
    ]
}

Each result is one Markdown section of the retrieved context, and metadata.section names it:

sectionContent
hintA hint about the type of question, for the answering model.
cypher_resultsResults of graph queries generated from the question.
entitiesUp to 25 entities matched by the query, with their descriptions.
relationshipsUp to 20 relations of those entities, one and two hops away.
factsUp to 15 facts found by vector search over the graph's relations.
passagesUp to 15 passages of the source documents, reranked by similarity to the query.

A section with nothing to report is left out. score is always null. The request body has no parameter to change the number of results.

Ask a Question

POST /v1/graph/completion retrieves context from the graph in the same way, then has graph.llm_model answer the question from that context. sources lists the retrieved sections the answer was based on, in the same shape as the results of a search:

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/completion' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "When did she join?",
  "history": [
    {"role": "user", "content": "Who is the CTO of Acme Corp?"},
    {"role": "assistant", "content": "Alice Smith is the CTO of Acme Corp."}
  ]
}'

Response:

{
    "answer": "Alice Smith joined Acme Corp as CTO in March 2026.",
    "sources": [
        {
            "content": "## Source Document Passages\nAcme Corp hired Alice Smith as its CTO in March 2026.",
            "score": null,
            "metadata": { "section": "passages" }
        }
    ]
}

history is optional. Pass the earlier turns of a conversation in it, each with a role of system, user, or assistant, so the model resolves follow-up questions such as "she" in the example above.

Query Errors

When search or completion fails, the endpoint returns an OpenAI-style error body with one of the following statuses. The underlying GraphRAG-SDK error, which contains internal hosts and ports, goes to the server log only.

StatusMessageCause
503Knowledge graph database is unavailable.The server cannot reach FalkorDB.
504Knowledge graph query timed out.The LLM, the embedding model, or the retrieval itself took too long.
502Graph LLM model 'MODEL' not found on infra.graph.llm_model names a model that DeepFellow Infra does not serve.
502Graph LLM request failed.Any other LLM error.
502Graph embedding request failed.The embedding model returned an error.
502Knowledge graph query failed.Any other retrieval error.

Entity Deduplication

After each batch of documents ingests, the server merges entities of the same type whose names are identical, ignoring case and whitespace. "Acme Corp" and "acme corp" become one entity, while "Acme Corp" and "Acme Corporation" stay separate.

To also merge near-identical names, set graph.fuzzy_dedup_enabled to true. The finalization step then embeds every entity name in the graph and merges entities of the same type whose names reach a cosine similarity of graph.fuzzy_dedup_threshold.

Fuzzy merges cannot be undone. A threshold that is too low merges different entities whose names only look alike, such as two people with similar names. Fuzzy deduplication also re-embeds every entity name in the project's graph after each ingestion, so ingestion takes longer as the graph grows.

Remove Content from the Graph

To remove every graph document of a file, together with any entities no other document refers to, call:

curl -X 'DELETE' \
  'https://deepfellow-server-host/v1/graph/files/FILE_ID' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

The endpoint returns 204 No Content, deletes the file's ingestion jobs, and sets the file's graph_ingestion_status back to null. The file itself, and any vector stores it belongs to, stay untouched. The endpoint returns 409 Conflict while an ingestion of the file is in progress. The trash button on the Knowledge Graph page calls this endpoint.

To remove a single graph document, pass its document_id from the ingestion job:

curl -X 'DELETE' \
  'https://deepfellow-server-host/v1/graph/documents/FILE_ID:INDEX' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

This endpoint also returns 204 No Content, and succeeds even if the document is not in the graph. The ingestion job lists the document as removed. To add it back, retry the document. A document removed while it is ingesting is removed from the graph once its ingestion finishes.

Deleted Files and Archived Projects

While graph.enabled is true, the server also removes graph data when its source goes away:

  • Deleting a file with DELETE /v1/files/{file_id} removes the file's graph documents and its ingestion jobs.
  • Archiving a project with POST /v1/organization/projects/{project_id}/archive drops the project's whole knowledge graph, its ontology, and its ingestion jobs. Archiving is permanent, and so is this removal.

An ingestion of the file or project that is running at that moment stops and undoes what it wrote.

This cleanup is best-effort. If it fails, for example because FalkorDB is unreachable, the file is still deleted, or the project still archived, and the server logs the failure.

To catch data that a failed or interrupted cleanup left behind, the server runs a sweep every graph.orphan_sweep_interval_seconds (one hour by default). Interrupted cleanups include an ingestion worker stopped partway through an ingestion. The sweep does the following:

  • Drops the knowledge graph and ontology of every archived project
  • Removes graph documents whose file no longer exists
  • Leaves alone graphs that don't belong to any project in the DeepFellow Server database, because another application or environment sometimes shares the FalkorDB instance

A new interval applies after the current wait ends.

Availability

Every endpoint on this page, and every endpoint in Knowledge Graph Ontology, returns 503 Service Unavailable with Knowledge graph is not configured while graph.enabled is false. The exceptions are the read-only endpoints below, which read from MongoDB only and work regardless:

  • GET /v1/graph/ingestion_jobs/{job_id}
  • GET /v1/graph/files/{file_id}/ingestion_job
  • GET /v1/graph/ontology

With RabbitMQ disconnected, the endpoints that queue work (ingest, retry, and ontology discovery) return 503 Service Unavailable as well, while search, completion, and removal keep working. See Nightshift Background Jobs.

Test Your Setup

The DeepFellow Server repository includes a script that verifies the whole pipeline against a running server. It uploads a sample text file, ingests it, waits for the job to finish, runs a few searches and questions, and then deletes the first ingested graph document with DELETE /v1/graph/documents/{document_id}:

uv run python scripts/test_graph.py --url http://localhost:8000 --key DEEPFELLOW_PROJECT_API_KEY
FlagDescription
--urlServer base URL. Defaults to http://localhost:8000.
--keyProject API key. Required.
--filePath to a text file to ingest instead of the built-in sample.
--queryQuestion to ask instead of the built-in ones. Repeat the flag to ask several.
--keepSkips deleting the graph document at the end.

The script leaves the uploaded file in place, together with the rest of its graph documents. To clean up afterward, remove the file from the graph with DELETE /v1/graph/files/{file_id} and then delete it with DELETE /v1/files/{file_id}.

We use cookies on our website. We use them to ensure proper functioning of the site and, if you agree, for purposes such as analytics, marketing, and targeting ads.