Skip to main content

VikingRAG 0.5.2

VikingRAG

Structure-aware retrieval for documents that actually have structure.

Independent Postgres/pgvector VikingRAG with a Python SDK and explicit control over retrieval budgets and experience learning.
Inspired by the VikingRAG paper · research artifacts: rucdatascience/VikingRAG (AGPL — not vendored here).

GitHub stars   GitHub forks   GitHub watchers

Release v0.5.2 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-oriented 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)
Controllable learning LearningPolicy: off / record_only / learn / frozen + snapshots
Operational constraints Auth allowlist, migrations, prod Compose, eval smoke, typed settings

Independent Apache-2.0 implementation — not a fork of the AGPL research artifacts. Paper charts below are paper experiments, not measurements from this repository. Score claims require regenerable exports under docs/EVALUATION.md.

Paper Table 3: token cost vs highest-accuracy baseline

Paper Table 3: VikingRAG-E+ token ratios vs gold/silver baselines

Charts redrawn from Gao et al., arXiv:2609.11390 Table 3 / abstract. Paper experiments — not measurements from this repository. Regenerate: uv run python scripts/render_paper_comparison_charts.py


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
# Offline wiring only (not semantic quality):
export VIKINGRAG_LLM_PROVIDER=fake
export VIKINGRAG_EMBEDDING_PROVIDER=deterministic
# Real retrieval: openai | deepseek | gemini | anthropic (+ matching embeddings)

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

Option B — Clone + Docker (API already on :8000)

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
# Compose already serves the API at http://localhost:8000 — do not also `make dev` (port conflict).

Host-only API with reload (when Compose API is not running):

make dev

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

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

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
  • Learning policy - hold-out scoring with frozen; warm-up with learn; snapshots freeze/rollback
  • Explain / replay - GET /v1/answers/{id}/explain and rag.explain(query_id) (offline, no new LLM calls)
  • Evaluation scaffolding - flat_rag + three VikingRAG modes; Voca FAQ fixtures; CSV/JSON exports; offline smoke
  • 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 - async with VikingRAGClient.from_settings() → ingest / index / ask / explain
  • 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. Defaults below are offline wiring (fake LLM + deterministic embeddings) — fine for API plumbing, not real semantic quality:

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

Embed VikingRAG in-process (no FastAPI server required). Still needs Postgres + Redis + env:

import asyncio
from vikingrag import VikingRAGClient

async def main() -> None:
    async with VikingRAGClient.from_settings() as rag:
        doc = await rag.ingest("policy.md")
        await rag.index(doc.id)
        answer = await rag.ask(
            "What is the cancellation policy?",
            document_ids=[doc.id],
            mode="vikingrag",
            learning_policy="record_only",  # held-out scoring: use "frozen"
        )
        print(answer.status, (answer.answer or "")[:200])
        run_id = answer.metadata.get("experience_query_run_id") or answer.query_id
        print(await rag.explain(run_id))

asyncio.run(main())

See also examples/voca_faq/. Advanced services remain on the client (search, answer_generator(), …).

Providers via env: VIKINGRAG_LLM_PROVIDER=openai|deepseek|gemini|anthropic|vllm (and matching embeddings). Empty VIKINGRAG_LLM_MODEL uses the provider preset (e.g. Gemini → gemini-2.5-flash). 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; learning_policy)
GET /v1/answers/{query_run_id}/explain Offline replay of a persisted run (auth-scoped)
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.5.2

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.5.2
File Size Uploaded
vikingrag-0.5.2.tar.gz 156.5 kB Details

Built distribution (wheel)

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

Total release size: 383.5 kB

Release files / vikingrag-0.5.2.tar.gz

Download URL vikingrag-0.5.2.tar.gz
Size 156.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f9e1a744454c807af3ff589853f85db914478bb9d7ee9b91015650e00e69d267
BLAKE2b-256 checksum
How to use checksums
9287513dd82265f38472dad764d866806d5e47c9f305793acea986c41e0253d4
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.5.2-py3-none-any.whl

Download URL vikingrag-0.5.2-py3-none-any.whl
Size 227.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
616acc3d5349c93912e2138b92861b1f3eaa795ea69b35c0708f2b3a479e4adb
BLAKE2b-256 checksum
How to use checksums
7c1423c753c81cdb2728db18ba24a95a3ee7c6ef03c0c67ad18311189a1ef1e3
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

This release

0.5.2 This release

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

0.4.1

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