Skip to main content

TrifectaRAG

Local multi-modal RAG: HNSW (vectors) + BM25 (keywords) + a knowledge graph, fused with Reciprocal Rank Fusion and optionally reranked with MMR.

Install the Python SDK and CLI from PyPI, or run the optional browser tutor from this repo.

pip install trifectarag[pdf]
from trifecta import TrifectaClient, PDFIngestor

client = TrifectaClient(device="cpu")
PDFIngestor(client, mode="page").ingest_pdf("textbook.pdf", output_dir="extracted")
client.save_snapshot("my.index")

hits = client.get_results(client.query("Newton's method", top_k=5))
for row in hits:
    print(row["global_id"], row["score"], row["metadata"].get("page"))
trifecta ingest textbook.pdf --index ./my.index
trifecta query "Newton's method" --index ./my.index --top-k 5
trifecta info --index ./my.index

Install

Requires Python 3.10+ and a C++17 toolchain only when building from source. PyPI wheels ship the compiled trifecta_py extension.

Extra What it adds
(none) Core SDK + CLI (hash embeddings, no PyTorch)
[pdf] PyMuPDF ingestion + vector-figure extraction
[ml] CLIP embeddings via torch / transformers
[mcp] Model Context Protocol server
[agent] Ollama tutor agent helpers
[all] Everything above
pip install trifectarag          # library + CLI
pip install trifectarag[pdf]     # + PDF ingestion
pip install trifectarag[all]     # + CLIP, MCP, agent

From a clone of this repository:

pip install -e ".[pdf,ml]"

Default index path is ./trifecta.index (or $TRIFECTA_INDEX). Snapshots are two files: <stem>.trifecta (C++ engine) and <stem>.meta.gz (Python metadata).


Python SDK

from trifecta import TrifectaClient, PDFIngestor
from trifecta import trifecta_py as tr

client = TrifectaClient(device="cpu")

gid = client.add_document(
    "Lagrange interpolation uses basis polynomials L_i(x).",
    metadata={"source": "notes", "page": 1},
)
client.add_image("figure.png", caption="Bisection interval", metadata={"source": "notes", "page": 2})
client.add_edge(gid, other_gid, tr.EdgeType.RELATES_TO)  # or EXPLAINS / DEPICTS

# PDF: page mode = one chunk per page + cropped figures. HNSW + BM25 are filled together.
stats = PDFIngestor(client, mode="page").ingest_pdf("textbook.pdf", output_dir="extracted")
print(stats)  # pages, text_chunks, images, kg_edges

client.save_snapshot("my.index")
client = TrifectaClient.from_snapshot("my.index", device="cpu")

mode="classical" uses overlapping word chunks instead of one page per node.

Query

hits = client.query(text="Lagrange interpolation", top_k=8)
for row in client.get_results(hits):
    meta = row["metadata"]
    print(row["global_id"], row["score"], meta.get("source"), meta.get("page"))

# Keyword only / vector only
client.query(text="bisection", top_k=5, use_hnsw=False, use_bm25=True)
client.query(text="bisection", top_k=5, use_hnsw=True, use_bm25=False)

# Image + text late fusion (CLIP, or hash embeddings if [ml] is not installed)
client.query(text="root finding figure", image="scan.png", top_k=5)

Page index

client.list_sources()
client.list_pages("Numerical_Analysis")
client.get_page_chunks("Numerical_Analysis", 36)
client.get_chunk_page(209)  # -> ("Numerical_Analysis", 213)

Hybrid search the tutor uses (MMR + provenance)

from trifecta import hybrid_search, serialize_sources

hits = hybrid_search(client, "Lagrange interpolation", top_k=4)
print(serialize_sources(hits))

CLI

trifecta ingest FILE.pdf [FILE.pdf ...] [--mode page|classical] [--pages START:END]
trifecta query TEXT [--image FILE] [--top-k N] [--no-hnsw] [--no-bm25]
trifecta add-text TEXT [--source NAME] [--page N]
trifecta add-image FILE [--caption TEXT]
trifecta info | sources | pages [--source NAME] [--page N]
trifecta get GID
trifecta mcp [--ingest FILE.pdf]

Shared flags: --index PATH, --device cpu|cuda, --json.

trifecta ingest examples/data/Numerical_Analysis.pdf --index ./na.index --pages 11:444
trifecta query "trapezoidal rule" --index ./na.index --json
python -m trifecta info --index ./na.index

How retrieval works

On ingest, HNSW and BM25 are always built together. At query time:

  1. Optional HNSW (vector / embedding search)
  2. Optional BM25 (lexical search)
  3. Knowledge-graph 1-hop expansion from those seeds
  4. Reciprocal rank fusion, then optional MMR so near-duplicate pages are dropped

Each hit carries source, page, global_id, and RRF score.


Browser tutor (this repo)

The optional UI is not on PyPI. From a clone:

  • Python 3.11+
  • Node 18+
  • Ollama with a tool-capable model (default qwen2.5:7b)
pip install -e ".[all]"
pip install -r requirements.txt
ollama pull qwen2.5:7b
cd frontend
npm install
# terminal 1 — API (port 8001)
python api.py

# terminal 2 — Vite (port 5172)
cd frontend
npm run dev

Open http://localhost:5172/. On first start the API loads a textbook snapshot under examples/data/ if one exists (*_page_*.trifecta).

Variable Default Meaning
OLLAMA_URL http://127.0.0.1:11434 Ollama server
OLLAMA_MODEL / TRIFECTA_OLLAMA_MODEL qwen2.5:7b Chat model
OLLAMA_CHAT_TIMEOUT_SECONDS 150 Per agent turn
TRIFECTA_AGENT_ROUNDS 6 Max tool-call rounds

Index a file from Library, then ask exam / figure / web questions in the single chat. Retrieval toggles (HNSW / BM25) live in the sidebar.

HTTP API (what the UI calls)

Base URL with Vite: same origin. Direct: http://127.0.0.1:8001.

Method Path Use
GET /health Chunks, corpus, retriever label
POST /chat Agent: { mode, messages, question?, attachments? }
POST /upload Index PDF/DOCX/TXT/MD/CSV → { task_id }
GET /ingest-status/{task_id} Poll until done / error
GET /corpora Indexed + on-disk sources
GET/POST /settings/retrieval { use_hnsw, use_bm25 }

Example scripts

From the repo root, after pip install -e ".[pdf,ml]":

python examples/00_basic_usage.py
python examples/01_ingest_textbook.py
python examples/02_query_textbook.py
python examples/05_page_index_demo.py

Put a PDF in examples/data/ or set TRIFECTA_TEXTBOOK_PDF.


Publishing

pip install build twine
python -m build
python -m twine check dist/*
python -m twine upload dist/*

GitHub Releases trigger .github/workflows/publish.yml (cibuildwheel + trusted PyPI publishing).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trifectarag-0.1.0.tar.gz (371.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trifectarag-0.1.0-cp312-cp312-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.12Windows x86-64

File details

Details for the file trifectarag-0.1.0.tar.gz.

File metadata

  • Download URL: trifectarag-0.1.0.tar.gz
  • Upload date:
  • Size: 371.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.4

File hashes

Hashes for trifectarag-0.1.0.tar.gz
Algorithm Hash digest
SHA256 31bce412b21dd35fb513412b04cb8a58194e792858c4f381d8b2c2826d1c2115
MD5 2ee1a07e1a9ae83cd16a811020e75eba
BLAKE2b-256 f73569c711659ab5849186fde5decf6b70469f3140d637ae6308553ec28fd2f5

See more details on using hashes here.

File details

Details for the file trifectarag-0.1.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for trifectarag-0.1.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 69eb14540bdfe3f3d18d48dc810ebd9f58d0e0d7b1db563159899dc9fbb431f0
MD5 4982d244df0b7567c781196947aaf769
BLAKE2b-256 4c17de45a615d6fd56e362b4d337daaa18fcd22c8cc7f9d428db31cce0c39487

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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