Skip to main content

VikingRAG 0.4.1

VikingRAG

Structure-aware retrieval for documents that actually have structure.

Production hierarchical RAG inspired by the VikingRAG paper.
Hierarchy-preserving ingestion. Multi-granular semantic Search. Built to extend.

GitHub stars   GitHub forks   GitHub watchers

Release v0.4.1 Apache 2.0 Python 3.12+ FastAPI PostgreSQL + pgvector Redis PyPI arXiv paper

Install · Features · Architecture · Quick start · SDK · API · Docs · Cite

LinkedIn   mhuzaifa.com   GitHub


Why VikingRAG?

Classic RAG often flattens documents into unrelated chunks. Structure is lost - sections, subsections, and provenance disappear into a top-k bag of text.

VikingRAG (Gao et al., 2026) showed that hierarchy-preserving storage, multi-granular indexing, evidence-gap tools (Search / List / Grep / Read), and adaptive escalation can improve accuracy while cutting tokens.

This project turns those ideas into a production-ready stack:

Research idea This repo today
Hierarchical documents document → section → subsection → chunk with stable + canonical object URIs
Multi-granular indexing Bottom-up abstracts + embeddings at document/section/chunk
Semantic Search pgvector cosine discovery returning viking:// URIs
Cheap path / agents Algorithm 1 agentic loop; E+ one-round fast path then escalate
Evidence-first grounding Authoritative Reads, citations, POST /v1/answers
Experience edges Algorithm 2 construction + Algorithm 3 Search+ (γ-gated)
Production constraints Auth allowlist, migrations, prod Compose, eval smoke, typed settings

Independent implementation inspired by the paper - not a fork of the AGPL research artifacts.


Install

Option A — PyPI (fastest on any laptop)

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -U vikingrag
# optional richer DOCX parsing:
# pip install "vikingrag[docx]"

You still need Postgres + pgvector and Redis, plus a .env (copy from the repo .env.example):

export VIKINGRAG_DATABASE_URL=postgresql+asyncpg://vikingrag:vikingrag@localhost:5432/vikingrag
export VIKINGRAG_REDIS_URL=redis://localhost:6379/0
export VIKINGRAG_LLM_PROVIDER=fake          # or openai / deepseek / gemini / anthropic
export VIKINGRAG_EMBEDDING_PROVIDER=deterministic

vikingrag-migrate upgrade head
vikingrag-api
# API: http://localhost:8000/docs

SDK:

from vikingrag import VikingRAGClient
client = VikingRAGClient.from_settings()

Option B — Clone + uv (full stack / contributors)

git clone https://github.com/mhuzaifadev/VikingRAG.git
cd VikingRAG
cp .env.example .env
# Python 3.12+, https://docs.astral.sh/uv/
make install && make docker-up && make migrate && make dev

Option C — pip from GitHub (pre-release / specific tag)

pip install "git+https://github.com/mhuzaifadev/VikingRAG.git@v0.4.1"

Option D — Docker Compose

git clone https://github.com/mhuzaifadev/VikingRAG.git && cd VikingRAG
cp .env.example .env
docker compose up -d --build

Production-shaped: docker compose -f docker-compose.prod.yml up -d --build — see docs/OPERATIONS.md.


Features

  • Hierarchical documents - preserve section / subsection / chunk structure instead of flattening everything
  • Stable URIs - address any node with viking://documents/... (storage-independent)
  • Structure-aware ingestion - Markdown, TXT, and PDF with region-local chunking
  • Idempotent uploads - skip_identical, replace, or create_version
  • Structural navigation - parent, children, ancestors, descendants, and URI resolve
  • Hierarchical abstracts - bottom-up summaries with token budgets and idempotent reuse
  • Multi-granular embeddings - chunk content + structural summaries with explicit embedding identity
  • Semantic Search - pgvector cosine retrieval with granularity weights and URI-addressable hits
  • List / Grep / Read - structural children, scoped literal Grep, authoritative Read with provenance
  • Evidence collection - bounded Search→List/Read pipeline assembling deduplicated evidence bundles
  • Evidence sufficiency - structured assessment (sufficient / insufficient / unknown) with citation checks
  • Agentic Algorithm 1 - tool-calling Search/List/Grep/Read loop with shared budgets and finalization reserve
  • Answers API - POST /v1/answers and POST /v1/query with modes vikingrag / vikingrag_e / vikingrag_e_plus
  • Experience edges - Algorithm 2 learning jobs + Algorithm 3 Search+ expansion
  • Evaluation scaffolding - six dataset adapters + offline smoke (vikingrag-eval smoke --offline)
  • Auth - optional API-key + server document allowlist (single-tenant)
  • Shared budgets - cumulative tool/embedding/read/LLM limits across a request
  • Replaceable providers - openai / deepseek / gemini / anthropic / vllm; fakes rejected in production
  • SDK facade - VikingRAGClient for embed-in-process use without FastAPI
  • Production foundation - FastAPI, PostgreSQL + pgvector, Redis, object store, typed settings, health checks

Docs hub: docs/README.md · docs/RETRIEVAL.md · docs/PAPER_PARITY.md · AGENTS.md


Architecture

flowchart TB
    Client[Client / SDK / UI]
    API[FastAPI Gateway]

    subgraph Ingest[Ingestion]
      Parse[Parsers TXT / MD / PDF]
      Hier[Hierarchy builder]
      Chunk[Structure-aware chunker]
      Persist[(PostgreSQL + object store)]
    end

    subgraph Retrieve[Retrieval]
      Search[Search]
      List[List]
      Grep[Grep]
      Read[Read]
      Judge[Evidence sufficiency]
      Agent[Bounded agent]
    end

    Client --> API
    API --> Ingest
    API --> Retrieve
    Parse --> Hier --> Chunk --> Persist
    Search --> Persist
    List --> Persist
    Grep --> Persist
    Read --> Persist
    Judge -->|insufficient| Agent

Document model

Documents are not flat chunk lists:

flowchart TD
    D["DOCUMENT<br/>viking://documents/{id}"]
    S1[SECTION · Storage]
    S2[SECTION · Retrieval]
    S3[SECTION · Operations]
    SS1[SUBSECTION · PostgreSQL]
    SS2[SUBSECTION · Object Storage]
    SS3[SUBSECTION · Semantic Search]
    C1[CHUNK]
    C2[CHUNK]

    D --> S1
    D --> S2
    D --> S3
    S1 --> SS1
    S1 --> SS2
    S2 --> SS3
    SS1 --> C1
    SS1 --> C2

Stable URIs:

viking://documents/{document_id}
viking://documents/{document_id}/nodes/{node_id}

Ingestion pipeline

flowchart LR
    A[Upload] --> B[Hash / idempotency]
    B --> C[Parse]
    C --> D[Structure extract]
    D --> E[Normalize]
    E --> F[Hierarchy]
    F --> G[Chunk in-region]
    G --> H[Persist nodes + blob]

Layout

src/vikingrag/
  api/             FastAPI routes & schemas
  application/     Use-cases (ingest, navigation)
  domain/          Models, URIs, errors
  ingestion/       Parse → hierarchy → chunk pipeline
  infrastructure/  Postgres, Redis, object store
  providers/       LLM / embedding / rerank Protocols
  observability/   Structured logging + correlation
  settings/        Typed configuration

Quick start

After Install, check health:

curl -s http://localhost:8000/health/live  | jq
curl -s http://localhost:8000/health/ready | jq

Ingest a document, index it, and search (with .env defaults: VIKINGRAG_LLM_PROVIDER=fake, VIKINGRAG_EMBEDDING_PROVIDER=deterministic):

curl -s -X POST http://localhost:8000/v1/documents \
  -F "file=@tests/fixtures/system_architecture.md;type=text/markdown" \
  -F "strategy=skip_identical" | jq

DOC_ID="<document_id from response>"
curl -s "http://localhost:8000/v1/documents/${DOC_ID}/tree" | jq

curl -s -X POST "http://localhost:8000/v1/documents/${DOC_ID}/index" \
  -H "Content-Type: application/json" \
  -d '{"force_summaries":false,"force_embeddings":false}' | jq

curl -s -X POST http://localhost:8000/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query":"How do we verify retrieved evidence?","top_k":5}' | jq

curl -s -X POST http://localhost:8000/v1/answers \
  -H "Content-Type: application/json" \
  -d "{\"query\":\"How do we verify retrieved evidence?\",\"document_ids\":[\"${DOC_ID}\"],\"execution_mode\":\"vikingrag\"}" | jq

Interactive API docs: http://localhost:8000/docs · Ops: docs/OPERATIONS.md


SDK

import asyncio
from vikingrag import VikingRAGClient

async def main() -> None:
    client = VikingRAGClient.from_settings()
    try:
        # hits = await client.search.search(...)
        # answer = await client.answer_generator().generate(...)
        pass
    finally:
        await client.aclose()

asyncio.run(main())

Providers via env: VIKINGRAG_LLM_PROVIDER=openai|deepseek|gemini|anthropic|vllm (and matching embeddings). Production rejects fake / deterministic / scripted.


API

Method Path Purpose
GET /health/live Liveness
GET /health/ready Readiness (Postgres + Redis)
POST /v1/documents Multipart ingest (.md / .txt / .pdf / .docx)
GET /v1/documents/{id} Document metadata
GET /v1/documents/{id}/tree Structural tree
POST /v1/documents/{id}/index Summarize + embed (idempotent)
GET /v1/documents/{id}/index-status Index stage and counts
POST /v1/search Semantic Search (scope_uri optional)
POST /v1/retrieval/list List direct children
POST /v1/retrieval/grep Scoped Grep
POST /v1/retrieval/read Authoritative Read
POST /v1/retrieval/evidence Collect evidence + assess sufficiency
POST /v1/answers Cited answer (vikingrag / vikingrag_e / vikingrag_e_plus)
POST /v1/query Mode-switched query orchestration
GET /v1/nodes/{id} Node metadata (+ optional content)
GET /v1/uris/resolve?uri= Resolve a viking://… URI

Development

Command Purpose
make install Sync deps (uv) + pre-commit
make dev API with reload
make lint Ruff
make typecheck mypy (strict)
make test Unit tests
make test-integration Integration tests (Postgres + Redis)
make migrate Apply Alembic migrations
make docker-up / make docker-down Compose stack (volumes preserved on down)
uv run vikingrag-eval smoke --offline Offline evaluation scaffolding smoke

Citation

If you use ideas from VikingRAG or this software in research, please cite the paper:

@article{gao2026vikingrag,
  title   = {VikingRAG: Accurate and Token-efficient Retrieval-augmented
             Generation over Structured Documents},
  author  = {Gao, Peiyuan and others},
  year    = {2026},
  eprint  = {2609.11390},
  archivePrefix = {arXiv},
  primaryClass  = {cs.CL},
  url     = {https://arxiv.org/abs/2609.11390}
}

Paper: arXiv:2609.11390 · Machine-readable: CITATION.cff · Legal notices: NOTICE


License

Apache License 2.0 - see LICENSE.

VikingRAG is independently authored for production use. Do not copy code from AGPL research repositories into this tree unless you intentionally accept those obligations.


Contributing

Issues and PRs are welcome. Please:

  1. Keep layers clean (API ≠ domain ≠ adapters)
  2. Prefer typed contracts over dict[str, Any]
  3. Add tests with features
  4. Run make lint && make typecheck && make test before opening a PR

⭐ If this project helps you, starring the repo helps others find it.



LinkedIn · mhuzaifa.com · GitHub

built with curiosity   shipped with love

Metadata

Release files for vikingrag 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vikingrag 0.4.1
File Size Uploaded
vikingrag-0.4.1.tar.gz 133.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vikingrag 0.4.1
File Interpreter ABI Platform
vikingrag-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 334.1 kB

Release files / vikingrag-0.4.1.tar.gz

Download URL vikingrag-0.4.1.tar.gz
Size 133.0 kB
Tags Source
SHA-256 checksum
How to use checksums
2a4e717ced9c21d107a12c531e581d2a02777d1fed625a571972f3b72992f7e8
BLAKE2b-256 checksum
How to use checksums
219f0cb4c8de3e25c96d89a8230f010e34809139141aff742841c7dc97f24a3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / vikingrag-0.4.1-py3-none-any.whl

Download URL vikingrag-0.4.1-py3-none-any.whl
Size 201.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ba06330163da1619878840c143cedc429eea260d8427c72ca9654b133aef5c5
BLAKE2b-256 checksum
How to use checksums
469905ece20108076d1c9e713a8cba7e979196c11598416aed4333ae73881aec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

This release

0.4.1 This release

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page