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:
- 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.
- Grouping. The server merges consecutive chunks into graph documents. See How Chunks Become Graph Documents.
- Entity extraction. A GLiNER model, hosted on DeepFellow Infra as the
glinercustom service, finds entities in each document. GLiNER only looks for the entity types declared in the project's ontology. - Relation extraction. The LLM set in
graph.llm_modelverifies 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'swarnings. - 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:
| Service | Role | Where to Get It |
|---|---|---|
| FalkorDB | Stores the graphs. | Installed by deepfellow server install --falkordb-active, or any external FalkorDB instance. |
| Doc Chunker | Converts and chunks files before ingestion and ontology discovery. | Custom service on DeepFellow Infra. See Doc Chunker. |
| GLiNER | Extracts entities. | gliner custom service on DeepFellow Infra. See Custom Service. |
| LLM | Extracts relations, answers questions, and drafts ontologies. | Any chat model installed on DeepFellow Infra. |
| Embedding model | Embeds 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-activeThe 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 startPoint 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.
| Setting | Environment Variable | Description | Default |
|---|---|---|---|
graph.enabled | DF_GRAPH__ENABLED | Enables or disables Knowledge Graph. | false |
graph.host | DF_GRAPH__HOST | Hostname of the FalkorDB instance. | localhost |
graph.port | DF_GRAPH__PORT | Port of the FalkorDB instance. | 6379 |
graph.username | DF_GRAPH__USERNAME | FalkorDB username. Leave it empty for a local instance, which has no users by default. | empty |
graph.password | DF_GRAPH__PASSWORD | FalkorDB password. Masked in deepfellow server config get unless you pass --secret. | empty |
graph.ssl | DF_GRAPH__SSL | Connects to FalkorDB over TLS, for example to a hosted instance. | false |
graph.llm_model | DF_GRAPH__LLM_MODEL | Model on DeepFellow Infra used for relation extraction, retrieval, and answers. | gpt-4o-mini |
graph.embedding_model | DF_GRAPH__EMBEDDING_MODEL | Embedding model on DeepFellow Infra. | text-embedding-3-small |
graph.embedding_dim | DF_GRAPH__EMBEDDING_DIM | Vector size graph.embedding_model returns. | 1536 |
graph.entity_extractor_prefix | DF_GRAPH__ENTITY_EXTRACTOR_PREFIX | Endpoint prefix of the GLiNER custom service on DeepFellow Infra. | gliner |
graph.entity_extractor_timeout_seconds | DF_GRAPH__ENTITY_EXTRACTOR_TIMEOUT_SECONDS | Seconds the server waits for one response from the GLiNER custom service. | 30 |
graph.entity_extractor_threshold | DF_GRAPH__ENTITY_EXTRACTOR_THRESHOLD | GLiNER 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_retries | DF_GRAPH__ENTITY_EXTRACTOR_MAX_RETRIES | Retries of a GLiNER call that timed out, failed to connect, or got a 5xx response. See Retries and Failures. | 2 |
graph.document_min_chars | DF_GRAPH__DOCUMENT_MIN_CHARS | Size a group of chunks has to reach before a heading change starts a new graph document. | 1000 |
graph.document_max_chars | DF_GRAPH__DOCUMENT_MAX_CHARS | Size at which a group of chunks becomes a graph document, even without a heading change. | 8000 |
graph.fuzzy_dedup_enabled | DF_GRAPH__FUZZY_DEDUP_ENABLED | Merges entities with similar, not only identical, names after each ingestion. | false |
graph.fuzzy_dedup_threshold | DF_GRAPH__FUZZY_DEDUP_THRESHOLD | Cosine similarity, from 0 to 1, two entity names need for a fuzzy merge. | 0.95 |
graph.document_ingestion_concurrency | DF_GRAPH__DOCUMENT_INGESTION_CONCURRENCY | Maximum number of documents of one ingestion job that ingest at once. Minimum 1. See How It Works. | 4 |
graph.document_ingestion_max_attempts | DF_GRAPH__DOCUMENT_INGESTION_MAX_ATTEMPTS | Nightshift deliveries an ingestion job gets before it is marked failed. See Retries and Failures. | 3 |
graph.ingestion_stale_after_seconds | DF_GRAPH__INGESTION_STALE_AFTER_SECONDS | Seconds 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_model | DF_GRAPH__ONTOLOGY_DISCOVERY_LLM_MODEL | Model on DeepFellow Infra used to draft ontologies. | gpt-4o-mini |
graph.ontology_discovery_sample_chunks_per_doc | DF_GRAPH__ONTOLOGY_DISCOVERY_SAMPLE_CHUNKS_PER_DOC | Number of chunks ontology discovery reads from each sample file. | 3 |
graph.ontology_discovery_concurrency | DF_GRAPH__ONTOLOGY_DISCOVERY_CONCURRENCY | Number of LLM calls ontology discovery runs in parallel. | 4 |
graph.ontology_discovery_max_sample_files | DF_GRAPH__ONTOLOGY_DISCOVERY_MAX_SAMPLE_FILES | Maximum number of sample files per discovery. Values above 10 have no effect. | 10 |
graph.ontology_discovery_max_attempts | DF_GRAPH__ONTOLOGY_DISCOVERY_MAX_ATTEMPTS | Nightshift deliveries an ontology discovery gets before it is marked failed. | 3 |
graph.ontology_discovery_llm_call_attempts | DF_GRAPH__ONTOLOGY_DISCOVERY_LLM_CALL_ATTEMPTS | Attempts, 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_entities | DF_GRAPH__ONTOLOGY_DISCOVERY_MAX_NEW_ENTITIES | Maximum number of entity types, not already in the seed ontology, one discovery adds. 0 means no cap. | 0 |
graph.ontology_discovery_max_new_relations | DF_GRAPH__ONTOLOGY_DISCOVERY_MAX_NEW_RELATIONS | Maximum 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 Status | Without force | With ?force=true |
|---|---|---|
| None | Starts a new job. | Starts a new job. |
in_progress | Returns 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. |
completed | Returns "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 documents | Resumes 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 documents | Resumes 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
sourceset tojson, 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"
}| Field | Description |
|---|---|
status | Job 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_count | Number of graph documents the file was split into. 0 until chunking finishes. |
document_counts | Number 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[].status | Each 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[].content | Full text of the document as it was ingested. |
documents[].error | Error message of a failed document, otherwise null. |
documents[].warnings | Relations 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_error | Error 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_progressorcompleted. - 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
pendingdocuments. To ingest all of them, callPOST /v1/graph/files/{file_id}/ingestinstead. - 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:
section | Content |
|---|---|
hint | A hint about the type of question, for the answering model. |
cypher_results | Results of graph queries generated from the question. |
entities | Up to 25 entities matched by the query, with their descriptions. |
relationships | Up to 20 relations of those entities, one and two hops away. |
facts | Up to 15 facts found by vector search over the graph's relations. |
passages | Up 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.
| Status | Message | Cause |
|---|---|---|
503 | Knowledge graph database is unavailable. | The server cannot reach FalkorDB. |
504 | Knowledge graph query timed out. | The LLM, the embedding model, or the retrieval itself took too long. |
502 | Graph LLM model 'MODEL' not found on infra. | graph.llm_model names a model that DeepFellow Infra does not serve. |
502 | Graph LLM request failed. | Any other LLM error. |
502 | Graph embedding request failed. | The embedding model returned an error. |
502 | Knowledge 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}/archivedrops 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_jobGET /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| Flag | Description |
|---|---|
--url | Server base URL. Defaults to http://localhost:8000. |
--key | Project API key. Required. |
--file | Path to a text file to ingest instead of the built-in sample. |
--query | Question to ask instead of the built-in ones. Repeat the flag to ask several. |
--keep | Skips 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.