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).

Metadata

Release files for trifectarag 0.1.0

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

Source distribution (sdist)

Source distribution for trifectarag 0.1.0
File Size Uploaded Provenance Actions
trifectarag-0.1.0.tar.gz 371.0 kB Without provenance Details for trifectarag-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for trifectarag 0.1.0
File Interpreter ABI Platform Actions
trifectarag-0.1.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details for trifectarag-0.1.0-cp312-cp312-win_amd64.whl

Total release size: 1.6 MB

Release files / trifectarag-0.1.0.tar.gz

Download URL trifectarag-0.1.0.tar.gz
Size 371.0 kB
Tags Source
SHA-256 checksum
How to use checksums
31bce412b21dd35fb513412b04cb8a58194e792858c4f381d8b2c2826d1c2115
BLAKE2b-256 checksum
How to use checksums
f73569c711659ab5849186fde5decf6b70469f3140d637ae6308553ec28fd2f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.4

Release files / trifectarag-0.1.0-cp312-cp312-win_amd64.whl

Download URL trifectarag-0.1.0-cp312-cp312-win_amd64.whl
Size 1.2 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
69eb14540bdfe3f3d18d48dc810ebd9f58d0e0d7b1db563159899dc9fbb431f0
BLAKE2b-256 checksum
How to use checksums
4c17de45a615d6fd56e362b4d337daaa18fcd22c8cc7f9d428db31cce0c39487
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.4

Security for trifectarag 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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