Skip to content

About

Multimodal, multi-project knowledge-graph RAG as a service. Ingest PDFs, Office docs, images & audio into a queryable graph (Postgres + pgvector + Apache AGE). Provider-agnostic, pure REST API for n8n & AI agents. Dockerized, MIT.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

PolyGraphRAG

PolyGraphRAG: turning messy multimodal data into production-ready knowledge graphs

A production FastAPI service for multimodal, multi-project knowledge-graph RAG β€” ingest PDFs, Office docs, images, and audio; extract an entity/relationship knowledge graph; and query it with dual-level retrieval. Provider-agnostic, backed by Postgres + pgvector + Apache AGE.

Python 3.11 FastAPI Postgres Tests Pull from GHCR License LinkedIn

PolyGraphRAG wraps RAG-Anything and LightRAG in a deployable HTTP API and adds the things you need to actually run graph-RAG as a service: many isolated projects on one instance, asynchronous ingest jobs, pluggable model providers per role, file lifecycle management, and an interactive graph viewer.


πŸ“₯ Drop in any file β†’ a GraphRAG database (embeddings + knowledge graph)

No separate OCR service. No parsing pipeline to wire up. No extraction stack to babysit. One upload endpoint swallows documents, images, and audio alike β€” OCR, layout parsing, transcription, and entity extraction all happen inside this one service. Whatever you send comes out the other side as a GraphRAG store in Postgres: vector embeddings in pgvector and an entity/relationship knowledge graph in Apache AGE, both queryable together. 22 first-class, verified file types, and unknown extensions are still attempted (routed through MinerU, ingestion unverified).

Category How it's parsed Extensions
πŸ“„ Documents Vision model (Office β†’ PDF via LibreOffice) pdf Β· docx Β· pptx Β· xlsx
πŸ–ΌοΈ Images Vision model, per file jpg Β· jpeg Β· png Β· gif Β· bmp Β· tiff Β· webp
πŸ”Š Audio Whisper transcription mp3 Β· wav Β· m4a Β· ogg Β· flac Β· opus Β· webm
πŸ“ Text Ingested directly md Β· txt Β· csv Β· html

One pipeline, one store, one query surface. The extracted text is embedded into pgvector for semantic retrieval, and its entities/relationships are lifted into an Apache AGE knowledge graph β€” so queries traverse both the vectors and the graph, not just chunk similarity. Point it at a PDF, a screenshot, a spreadsheet, or a voice memo; the plumbing is already done.

Interactive knowledge-graph viewer for a workspace
The extracted knowledge graph for a workspace, rendered by the built-in interactive viewer.


πŸ”€πŸ€– The perfect memory & knowledge backend for n8n workflows and AI agents

Pure API, built for automators β€” every feature is an endpoint you can call. No app to adopt, no dashboard to learn: you get a clean, fully-documented API plus a Swagger UI at /docs to explore and test it, and an interactive graph viewer you can fetch as HTML and drop straight into your own app or agent UI when you want to show the knowledge graph. It's exactly what you want when the "user" is a workflow or an agent β€” nothing between you and the automation.

It's just HTTP + bearer auth β€” no SDK, no client library, no glue code. Every capability is a plain REST call, so it drops straight into an n8n HTTP Request node or an AI agent tool with zero adapters:

  • Ingest β€” wire a Google Drive / Gmail / webhook trigger straight into POST /workspace/{id}/upload/batch. Your automation feeds files in; PolyGraphRAG does the OCR, parsing, transcription, and graph-building.
  • Query as a tool β€” give your agent POST /workspace/{id}/query for a synthesized answer, or POST /workspace/{id}/query/data for structured evidence with no LLM answer β€” ideal when the agent wants raw facts to reason over instead of prose.
  • Isolate per client/topic β€” one workspace per project means an n8n workflow or a multi-tenant agent can keep every knowledge base cleanly separated on a single deployment.
  • A machine-readable contract β€” /openapi.json declares typed response schemas for every endpoint (including the canonical ingest-job status enum) and advertises the Bearer/Basic auth scheme, so a tool-calling agent can learn the entire API from the spec alone, no live probing needed.

Retrieval-augmented n8n flows and tool-calling agents get a multimodal, graph-aware knowledge store behind a single URL β€” point your workflow at it and ship.


Why it's interesting

  • πŸ—‚οΈ Multi-project β€” multiple isolated knowledge graphs. Every workspace is its own graph and its own vector namespace. Spin up as many projects as you like on a single deployment and ingest, query, and visualize each one completely independently. No cross-talk between corpora.
  • πŸ”Œ Provider-agnostic, per role. The extraction LLM, the query LLM, the vision model, embeddings, and Whisper are each independently pointed at OpenAI or any OpenAI-compatible endpoint (OpenRouter, DeepSeek, Ollama, vLLM, LM Studio, TEI…). Run cheap high-volume extraction on one provider and fast synthesis on another β€” configured entirely by environment variables.
  • 🧩 Genuinely multimodal. PDFs and images go through a vision model, Office docs via LibreOffice, audio via Whisper, and text/markdown/CSV directly β€” 22 file types, one upload endpoint.
  • πŸ•ΈοΈ A real knowledge graph, not just chunks. LightRAG extracts entities and relationships and stores them in Apache AGE (graph) + pgvector (embeddings) inside one Postgres. Query modes span local, global, hybrid, and naive retrieval, and query/data returns the structured evidence without an LLM answer.
  • βš™οΈ Built to operate. Async batch ingestion with per-file job tracking and integrity verification, soft-delete + restore of workspaces, per-file delete that correctly preserves entities shared with other documents, and a container HEALTHCHECK.
  • βœ… A fully mocked test suite (230+ tests). The suite stubs RAG-Anything, LightRAG, and the database, so pytest runs green with no Postgres and no API keys.

Architecture

flowchart LR
    client([Client / curl / app]) -->|HTTP| api[FastAPI service<br/>PolyGraphRAG]

    subgraph providers[Pluggable model providers &#40;per role&#41;]
      llm[Extraction + Query LLM]
      vis[Vision]
      emb[Embeddings]
      whis[Whisper]
    end

    api --> raga[RAG-Anything<br/>parsing + ingest pipeline]
    api --> lr[LightRAG<br/>graph build + retrieval]
    raga --> lr
    lr <--> llm
    raga <--> vis
    lr <--> emb
    raga <--> whis

    subgraph pg[Postgres]
      age[(Apache AGE<br/>knowledge graph)]
      vec[(pgvector<br/>embeddings)]
      meta[(file / job / workspace<br/>metadata)]
    end
    lr --> age
    lr --> vec
    api --> meta
Loading

Each workspace maps to a physical LightRAG namespace: the bootstrap default workspace (seeded only into an empty registry) uses the shared chunk_entity_relation AGE graph, and every workspace created via the API gets its own {workspace}_chunk_entity_relation graph plus workspace-scoped vector rows β€” that's what keeps projects isolated. Workspaces are peers; none is delete-protected.

Requires Postgres to run

PolyGraphRAG's storage backend is Postgres (pgvector + Apache AGE + pg_trgm) β€” it can't serve requests without it. You don't need to set that up by hand: docker compose up builds and starts a correctly-extended Postgres alongside the API. The only thing that runs without a database is the test suite, which is fully mocked.

Quickstart

cp .env.example .env          # set POSTGRES_PASSWORD + at least one API key
docker compose pull           # fetch prebuilt images from GHCR (fast path)
docker compose up -d          # start Postgres(+AGE) and the API
curl localhost:9622/health    # -> {"status":"ok"}

Prefer to build the images yourself (e.g. after changing the code)? Swap the middle two commands for docker compose up -d --build.

Open http://localhost:9622/docs for the full interactive Swagger UI.

Auth is off by default (the ports are loopback-only). To require credentials, set API_TOKENS in .env (comma-separated). Every endpoint except /health then needs a token: machines send -H "Authorization: Bearer <token>"; in a browser enter any username with the token as the password. Serve over TLS whenever you expose the service beyond loopback.

Scope: single-instance, low-usage service β€” run one worker/replica (ingest job state is in-process). Not designed for high load or horizontal scaling.

Ingest and query (end to end)

# 1. Create an isolated project/graph
curl -X POST localhost:9622/all-workspaces/create \
  -H 'content-type: application/json' \
  -d '{"id":"acme","name":"Acme Corp"}'

# 2. Upload one or more files (ingestion runs in the background)
curl -X POST localhost:9622/workspace/acme/upload/batch \
  -F 'files=@handbook.pdf'
# -> {"batch_id":"...","jobs":[{"job_id":"ab12cd34", ...}]}

# 3. Poll a job until it's "done" (statuses: pending/processing/retrying/done/failed)
curl localhost:9622/workspace/acme/status/ab12cd34

# 4. Query the knowledge graph
curl -X POST localhost:9622/workspace/acme/query \
  -H 'content-type: application/json' \
  -d '{"query":"How do we handle refunds?","mode":"mix"}'

# 5. Open the interactive graph in a browser
#    http://localhost:9622/workspace/acme/graph.html

Citations point at your real files. Upload with source_path + path_root metadata and every /query reference's file_path is the real, openable document path you provided (resolved from Postgres, not LightRAG's internal name) β€” so answers cite files your own tooling can open.

API at a glance

Area Endpoints
Discovery & health GET / (service card) Β· GET /health
Workspaces GET /all-workspaces/list Β· POST /all-workspaces/create Β· GET /workspace/{id} Β· DELETE /workspace/{id} Β· POST /workspace/{id}/restore
Ingestion POST /workspace/{id}/upload/batch Β· GET /workspace/{id}/batch/{batch_id} Β· GET /workspace/{id}/status/{job_id} Β· GET /workspace/{id}/jobs
Files GET /workspace/{id}/files Β· DELETE /workspace/{id}/file/delete
Query POST /workspace/{id}/query Β· POST /workspace/{id}/query/data
Visualization GET /workspace/{id}/graph.html

Full request/response details, query modes, and examples live in docs/api-reference.md.

Configuration

Everything is environment-driven. The headline knob is per-role provider routing: for each of LLM, QUERY_LLM, VISION, EMBEDDING, and WHISPER, an empty *_BASE_URL means OpenAI, and a set one points at any OpenAI-compatible endpoint authenticated with the matching *_API_KEY.

A cost-optimized example β€” DeepSeek (via OpenRouter) for the LLM, OpenAI for embeddings:

LLM_MODEL=deepseek/deepseek-chat
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=sk-or-...
EMBEDDING_MODEL=text-embedding-3-small     # blank EMBEDDING_BASE_URL -> OpenAI
OPENAI_API_KEY=sk-...

See .env.example for every variable and docs/configuration.md for the full routing matrix and caveats (e.g. changing EMBEDDING_MODEL/EMBEDDING_DIM repoints the vector tables and requires re-ingestion).

Testing

python -m venv .venv && . .venv/Scripts/activate   # or source .venv/bin/activate
pip install -r requirements-dev.txt
pytest -q                                          # all green β€” no Postgres, no keys

The suite mocks RAG-Anything, LightRAG, and the Postgres pool β€” no external services, no keys.

Two smoke tests live in scripts/:

python scripts/smoke_test.py          # in-process ASGI smoke (stubs, no DB/keys)
./scripts/smoke_test_docker.sh        # live HTTP smoke against a running stack

Project layout

.
β”œβ”€β”€ server/              # the FastAPI service (package: app + routers + services)
β”‚   β”œβ”€β”€ __init__.py      #   app assembly, lifespan, /health, router wiring
β”‚   β”œβ”€β”€ config.py        #   env parsing + provider routing
β”‚   β”œβ”€β”€ schemas.py       #   Pydantic request models
β”‚   β”œβ”€β”€ db.py            #   schema init + metadata persistence
β”‚   β”œβ”€β”€ workspaces.py    #   per-workspace RAGAnything instance registry
β”‚   β”œβ”€β”€ ingest.py        #   document parsing + ingestion pipeline
β”‚   β”œβ”€β”€ worker.py        #   background ingest worker
β”‚   β”œβ”€β”€ llm.py           #   LLM / embedding shims
β”‚   β”œβ”€β”€ references.py    #   citation + real-path resolution
β”‚   β”œβ”€β”€ graph.py         #   knowledge-graph HTML rendering
β”‚   β”œβ”€β”€ auth.py          #   opt-in API-token middleware
β”‚   β”œβ”€β”€ deps.py          #   shared endpoint dependencies
β”‚   └── routers/         #   query, documents, and workspace-registry endpoints
β”œβ”€β”€ Dockerfile           # app image
β”œβ”€β”€ requirements.txt     # runtime deps (lightrag-hku pinned to a release with the AGE fix)
β”œβ”€β”€ docker-compose.yml   # postgres (pgvector + AGE) + polygraphrag services
β”œβ”€β”€ db/                  # Postgres image build + init.sql (extensions)
β”œβ”€β”€ scripts/             # in-process + live-docker smoke tests
β”œβ”€β”€ tests/               # fully mocked tests (unit + end-to-end)
└── docs/                # architecture, API reference, configuration, LightRAG internals

Documentation

Upstream contribution

The one bug that used to force this project to vendor a 6,700-line fork of LightRAG's Postgres layer β€” Apache AGE silently dropping edge properties β€” was fixed upstream by Oleksandr Kushnir and merged into LightRAG as HKUDS/LightRAG#3052. PolyGraphRAG now runs stock, current LightRAG with no patch and no version-lock. The live smoke test confirms edges persist their full property maps (weight, keywords, description, source_id, file_path) on lightrag-hku==1.5.4.

Author

Oleksandr Kushnir β€” GitHub Β· LinkedIn

Credits & license

PolyGraphRAG is a service layer over RAG-Anything and LightRAG (both MIT) by HKUDS. Document parsing is powered by MinerU (MinerU Open Source License, Apache-2.0 based) by OpenDataLab.

Full attribution and the third-party license inventory are in NOTICE and THIRD_PARTY_LICENSES.md. PolyGraphRAG's own code is licensed under the MIT License.

About

Multimodal, multi-project knowledge-graph RAG as a service. Ingest PDFs, Office docs, images & audio into a queryable graph (Postgres + pgvector + Apache AGE). Provider-agnostic, pure REST API for n8n & AI agents. Dockerized, MIT.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages