🧠 pyragcore
A reusable, modular RAG (Retrieval-Augmented Generation) core library built on FAISS and Ollama. Use it as the foundation for any AI project that needs document ingestion, semantic search, and LLM-powered responses.
Features
- 🗂️ FAISS vector store with persistence, deduplication, and metadata filtering
- 🔢 SentenceTransformer embeddings with GPU support
- 🔍 Semantic retrieval with MMR search and metadata filtering
- 🤖 Ollama LLM integration for local, private inference
- 🎙️ Voice input/output support
- 🧱 Abstract base classes for building custom pipelines
- 📦 Modular optional dependencies — install only what you need
Requirements
- Python 3.13+
- Ollama installed and running (for LLM features)
- NVIDIA GPU with CUDA 12.8+ (optional, falls back to CPU)
Installation
pip install pyragcore # core only (FAISS + tqdm + langchain-text-splitters)
pip install pyragcore[embeddings] # + SentenceTransformers
pip install pyragcore[ollama] # + Ollama LLM
pip install pyragcore[voice] # + speech input/output
pip install pyragcore[all] # everything
Quick Start
from pyragcore.pipeline.base_pipeline import BasePipeline
from pyragcore.embeddings.sentencetransformerembedder import SentenceTransformerEmbedder
from pyragcore.retrieval.vector_store import FaissVectorStore
from pyragcore.retrieval.retriver import FaissRetriever
from pyragcore.llm.ollama_llm import Responder
# Extend BasePipeline for your use case
class MyPipeline(BasePipeline):
def ingest(self, source: str) -> str:
# implement your ingestion logic
...
pipeline = MyPipeline(persist_dir="./memory", output_folder="./output")
source_id = pipeline.ingest("./my_document.pdf")
answer = pipeline.ask("What is this document about?", source_id=source_id)
print(answer)
Architecture
pyragcore/
├── CHANGELOG.md
├── LICENSE
├── pyproject.toml
├── py.typed
├── README.md
└── pyragcore
├── embeddings
│ └── sentencetransformerembedder.py
├── exceptions.py
├── ingestion
│ └── chunker.py
├── interfaces
│ ├── base_chunker.py
│ ├── base_embedder.py
│ ├── base_llm.py
│ ├── base_loader.py
│ ├── base_retriever.py
│ └── base_vector_store.py
├── llm
│ ├── prompt.py
│ └── responder.py
├── pipeline
│ └── base_pipeline.py
├── retrieval
│ ├── retriver.py
│ └── vector_store.py
└── utils_io
├── choose_model.py
├── logger.py
└── voice.py
Building a Custom Pipeline
Extend BasePipeline and implement ingest():
from pyragcore.pipeline.base_pipeline import BasePipeline
from interfaces.base_loader import BaseLoader
from pyragcore.ingestion.chunker import Chunker
from tqdm import tqdm
class MyLoader(BaseLoader):
def read(self, path) -> dict:
# read your source and return
return {
"text": "...",
"metadatas": {
"file_id": "unique_id",
"file_name": "my_file.txt",
"source": path,
}
}
class MyPipeline(BasePipeline):
def __init__(self, persist_dir: str, output_folder: str, model_name: str = "llama3.2"):
super().__init__(persist_dir, output_folder, model_name)
self.chunker = Chunker()
def ingest(self, source: str) -> str:
loader = MyLoader()
content = loader.read(source)
text = content.get("text", "")
metadata = content.get("metadatas", {})
source_id = metadata.get("file_id", "")
if self._is_ingested(source_id):
print("Already ingested, skipping...")
return source_id
chunks = self.chunker.chunk(text, metadata)
documents, metadatas, ids = [], [], []
for i, item in enumerate(chunks):
documents.append(item["chunk"])
metadatas.append(item["metadatas"])
ids.append(f"{source_id}_chunk_{i}")
BATCH_SIZE = 64
all_embeddings = []
for start in tqdm(range(0, len(documents), BATCH_SIZE), desc="Embedding"):
batch = documents[start:start + BATCH_SIZE]
all_embeddings.extend(self.embedder.embed(batch))
self.vector_store.add(
embeddings=all_embeddings,
documents=documents,
metadata=metadatas,
ids=ids
)
return source_id
FaissVectorStore
from pyragcore.retrieval.vector_store import FaissVectorStore
store = FaissVectorStore(dim=768, persist_path="./memory", autosave=True)
# add documents
store.add(embeddings=[[...]], documents=["text"], metadata=[{"file_id": "abc"}], ids=["id_0"])
# search
results = store.search(query_embedding=[...], k=5)
# search with filter
results = store.search_with_filter(query_embedding=[...], k=5, where={"file_id": "abc"})
# MMR search for diversity
results = store.mmr_search(query_embedding=[...], k=5, lamda_param=0.5)
# list ingested files
files = store.list_files()
SentenceTransformerEmbedder
from pyragcore.embeddings.sentencetransformerembedder import SentenceTransformerEmbedder
embedder = SentenceTransformerEmbedder(model_name="all-mpnet-base-v2")
# embed multiple texts
embeddings = embedder.embed(["text one", "text two"])
# embed a single query
embedding = embedder.embed_one("what is a database?")
PyTorch with CUDA
pyragcore does not pin a specific PyTorch version to stay flexible. Install the version that matches your system:
# CUDA 12.8
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128
# CPU only
pip install torch torchvision
Exceptions
from pyragcore.exceptions import (
BotRagException, # base exception
EmbeddingException, # embedding failed
RetrievalException, # retrieval failed
VectorStoreException, # vector store error
ModelNotFoundException, # ollama model not found
)
Custom Backends (v0.2.0+)
You can now swap any component with your own implementation:
Custom SentenceTransformerEmbedder
from pyragcore import BaseEmbedder
class MyEmbedder(BaseEmbedder):
def embed(self, texts: list[str]) -> list[list[float]]:
# your implementation
...
def embed_one(self, text: str) -> list[float]:
...
def get_dimension(self) -> int:
return 768
if __name__=="__main__":
rag = RagPipeline("memory", "output", embedder=MyEmbedder())
Custom Vector Store
from pyragcore import BaseVectorStore
class MyVectorStore(BaseVectorStore):
def add(self, embeddings, documents, metadata, ids):
...
def search(self, query_embedding, k=5):
...
if __name__ =="__main__":
rag = RagPipeline("memory", "output", vector_store=MyVectorStore())
Projects Built with pyragcore
- StudyBot — Chat with your documents and YouTube videos
- Coder-Assistant — AI assistant for your codebase (WIP) (Soon)
Contributing
- Fork the repo
- Create a feature branch (
git checkout -b feature-name) - Commit your changes (
git commit -m "Add feature") - Push to the branch (
git push origin feature-name) - Open a Pull Request
License
Metadata
Release files for pyragcore 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyragcore-0.3.2.tar.gz | 26.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyragcore-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.5 kB
Release files / pyragcore-0.3.2.tar.gz
| Download URL | pyragcore-0.3.2.tar.gz |
|---|---|
| Size | 26.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fba92dd93e11a1e707952b5f3f0f96fea1e3c9d1fe7e0b85b9f372a5b0d16528
|
|
BLAKE2b-256 checksum How to use checksums |
6b929c17b06803d6a22353da0665966c4618e3e2bacd901955f98aa8a76c293f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pyragcore-0.3.2-py3-none-any.whl
| Download URL | pyragcore-0.3.2-py3-none-any.whl |
|---|---|
| Size | 24.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3da0fab600644953e7a453bd10d62d63c3892b0d90fdc232f50c0fe15d15c3be
|
|
BLAKE2b-256 checksum How to use checksums |
fcf314a6ac62dc5cb74103ee59d9b97efc1cd71897107e8857c26a77e911c559
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|