Skip to main content

SlimX-RAG: a slim, deterministic RAG indexing pipeline (ingest -> chunk -> embed -> index).

Project description

SlimX-RAG

A slim, deterministic RAG pipeline for customer-demo-ready private knowledge assistants.

What’s implemented today:

ingest → chunk → embed → index → retrieve → answer → cite → evaluate → serve


Install

This project is packaged as slimx-rag and ships a CLI command slimx-rag.

With uv (recommended)

uv sync

with extra dependencies

uv sync --group dev

Optional embedding providers:

uv sync --extra openai   # OpenAI embeddings via langchain-openai
uv sync --extra hf       # SentenceTransformers embeddings
uv sync --extra demo     # FastAPI customer demo server
uv sync --extra doc      # PDF/DOCX loaders

With pip

python -m pip install -e ".[dev]"
source venv/bin/activate  # or your venv path

Run the tests

After installing with dev extras, activate the venv:

source .venv/bin/activate

Then run:

# requires uv sync --group dev
pytest
# or
pytest -q

(Or use uv run pytest without activating)


Quickstart: run the full pipeline

  1. Use one of the example knowledge bases, or put Markdown documents under your own folder:
examples/tiny_demo/knowledge-base/
  overview.md
  1. Activate the venv:
source .venv/bin/activate

Then run:

slimx-rag run --kb-dir examples/tiny_demo/knowledge-base --out-dir ./output
slimx-rag ask --out-dir ./output --q "What is this knowledge base about?"

(Or use uv run slimx-rag run ... without activating)

Outputs:

  • output/docs.jsonl (raw documents + metadata)
  • output/chunks.jsonl (deterministic chunks)
  • output/embeddings.jsonl (vectors; default provider is offline hash)
  • output/index.jsonl (vector index records; for small corpora with the local backend)
  • output/index_state.json (embedding config and incremental index state)
  • output/manifest.json (optional RAGOps build manifest when requested)

Run as a Docker service

The Docker image is turnkey build-then-serve: point it at a knowledge-base directory and it builds the index on first start (with the local hf sentence-transformers embedder, baked into the image — no network needed at runtime), then serves /health, /api/retrieve, /api/ask, and /api/index on port 8080.

POST /api/index {workspace_id, document_id, text, metadata?} ingests a single document into the live index (chunk → embed → upsert) so a downstream app can index uploaded documents over HTTP instead of rebuilding from a KB dir. Identity is derived from workspace_id/document_id, so re-posting the same document is idempotent and a content change replaces just that document's chunks. (Retrieval is still over one global index — workspace_id/document_id are stored in chunk metadata for future scoping.)

# Build (or pull the published image: ghcr.io/slimx-ai/slimx-rag)
docker build -t slimxai/slimx-rag:hf .

# Index a KB + serve in one container
docker run --rm -p 8080:8080 \
  -e RAG_KB_DIR=/kb \
  -v "$PWD/examples/tiny_demo/knowledge-base:/kb:ro" \
  -v slimx_rag_index:/app/output \
  slimxai/slimx-rag:hf

curl localhost:8080/health   # {"status":"ok","embed_provider":"hf",...}

Or use the demo compose (builds the image, indexes the bundled corpus, serves):

docker compose -f docker-compose.demo.yml up --build

Set RAG_REINDEX=1 to rebuild the index on start. Override the embedder via RAG_EMBED_PROVIDER (hash | openai | hf) and related RAG_* env. The image is published to ghcr.io/slimx-ai/slimx-rag by .github/workflows/publish-image.yaml on release; downstream apps consume that image (they do not build it).


RAGOps artifacts

SlimX-RAG can generate reproducibility and inspection artifacts for deterministic, auditable RAG indexing:

  • manifest.json records what was built, including artifact counts, embedding settings, backend information, chunk config, and the centralized hash policy.
  • slimx-rag diff compares two builds by documents, chunks, and available config metadata.
  • slimx-rag report summarizes index quality, metadata coverage, duplicates, near-empty chunks, backend details, and warnings.

Generate and inspect RAGOps artifacts:

slimx-rag run --kb-dir examples/tiny_demo/knowledge-base --out-dir ./output --write-manifest
slimx-rag manifest --out-dir ./output
slimx-rag diff ./output-v1 ./output-v2
slimx-rag diff ./output-v1 ./output-v2 --format json
slimx-rag report --out-dir ./output --format markdown
slimx-rag report --out-dir ./output --format json

Expected RAGOps output files:

  • output/manifest.json — pretty JSON build manifest for reproducibility and audits.
  • slimx-rag diff ... — text or JSON comparison output; it does not write files by default.
  • slimx-rag report ... — Markdown or JSON report printed to stdout.

These modules are intended for reproducible, auditable RAGOps for private/local AI systems without changing the ingest, chunk, embed, index, or query artifacts.


Run modules one by one

1) Ingest

slimx-rag ingest --kb-dir examples/tiny_demo/knowledge-base --out ./output/docs.jsonl

Defaults:

  • file pattern: **/*.md

Override the glob:

slimx-rag ingest --kb-dir examples/tiny_demo/knowledge-base --glob "**/*.txt" --out ./output/docs.jsonl

2) Chunk

slimx-rag chunk --in ./output/docs.jsonl --out ./output/chunks.jsonl \
  --chunk-size 800 --chunk-overlap 120

3) Embed

Offline deterministic embeddings (good for CI/dev; not semantic):

slimx-rag index --in ./output/chunks.jsonl --embed-provider hash --embed-dim 384

OpenAI embeddings:

export OPENAI_API_KEY="..."
slimx-rag index --in ./output/chunks.jsonl --embed-provider openai --embed-model text-embedding-3-small

HuggingFace SentenceTransformers:

slimx-rag index --in ./output/chunks.jsonl --embed-provider hf --hf-model sentence-transformers/all-MiniLM-L6-v2

4) Index

The indexing layer is implemented as a backend plugin so you can switch storage/search engines without changing pipeline code.

Supported backends:

  • local — JSONL MVP backend (default): portable, deterministic, loads in-memory for query
  • faiss — local FAISS index (binary) + JSON sidecar for payloads (optional extra)
  • qdrant — remote Qdrant collection (optional extra)
  • pgvector — Postgres + pgvector table (optional extra)

Install optional backends

uv sync --extra faiss
uv sync --extra qdrant
uv sync --extra pgvector

CLI usage

Local (default):

slimx-rag run --kb-dir examples/insurance_demo/knowledge-base --out-dir ./output --index-backend local

FAISS (dimension can be inferred from the first vector; dim is optional but useful for empty-index creation):

slimx-rag run --kb-dir examples/insurance_demo/knowledge-base --out-dir ./output \
  --index-backend faiss \
  --backend-config '{"dim": 384}'

Qdrant:

slimx-rag run --kb-dir examples/insurance_demo/knowledge-base --out-dir ./output \
  --index-backend qdrant \
  --backend-config '{"url":"http://localhost:6333","collection":"slimx"}'

pgvector:

slimx-rag run --kb-dir examples/insurance_demo/knowledge-base --out-dir ./output \
  --index-backend pgvector \
  --backend-config '{"dsn":"postgresql://user:pass@localhost:5432/db","table":"slimx_vectors"}'

Use as a Python library

Ingest

from pathlib import Path
from slimx_rag.ingest.loader import fetch_documents
from slimx_rag.settings import IndexingPipelineSettings

settings = IndexingPipelineSettings(kb_dir=Path("examples/tiny_demo/knowledge-base"))
docs = fetch_documents(settings=settings)
print(len(docs), docs[0].metadata)

Chunk

from slimx_rag.chunk import chunk_documents
from slimx_rag.settings import ChunkSettings

cset = ChunkSettings(chunk_size=800, chunk_overlap=120)
chunks = chunk_documents(
    docs,
    chunk_size=cset.chunk_size,
    chunk_overlap=cset.chunk_overlap,
    separators=cset.separators,
    extended_chunk_metadata=cset.extended_metadata,
)
print(len(chunks), chunks[0].metadata["chunk_id"])

Embed

from slimx_rag.embed import embed_chunks
from slimx_rag.settings import EmbedSettings

eset = EmbedSettings(provider="hash", dim=384)
embs = list(embed_chunks(chunks, settings=eset))
print(embs[0].chunk_id, len(embs[0].vector))

Index + Query

from pathlib import Path
from slimx_rag.index import make_index_backend
from slimx_rag.embed import make_embedder
from slimx_rag.settings import EmbedSettings, IndexSettings

index_path = Path("./output/index.jsonl")
index_settings = IndexSettings(backend="local")
idx = make_index_backend(index_path, settings=index_settings)
idx.load()
eset = EmbedSettings(provider="hash", dim=idx.dim or 384)
qvec = make_embedder(eset).embed_texts(["what is this knowledge base about"])[0]
results = idx.query(list(map(float, qvec)), top_k=5)
print(results[0].chunk_id, results[0].score)

Output formats

docs.jsonl and chunks.jsonl

Each line is:

{"page_content": "...", "metadata": {"doc_id": "...", "kb_relpath": "...", "content_hash": "..."}}

embeddings.jsonl

Each line is:

{"chunk_id": "...", "vector": [0.1, -0.2, ...], "text": "...", "metadata": {"kb_relpath": "..."}}

index.jsonl

Each line is:

{"chunk_id": "...", "vector": [0.1, -0.2, ...], "text": "...", "metadata": {"kb_relpath": "..."}}

Design notes (why it’s deterministic)

  • Ingest assigns a stable doc_id primarily derived from kb_relpath (stable across machines).
  • Ingest also assigns content_hash derived from document text (changes when content changes).
  • Chunking sorts documents by a stable key (kb_relpath/source/doc_id) before splitting.
  • Chunk IDs are stable hashes of (parent identity, content_hash, chunk config, chunk_index).
  • Embedding can be run offline (hash) so tests and CI don’t depend on network calls.
  • Indexing stores vectors deterministically; the local backend computes vector norms when loading/querying for fast cosine search.

Project layout

src/slimx_rag/
  ingest/   # load docs + baseline metadata
  chunk/    # deterministic splitting + chunk_id
  embed/    # embedding providers + batching + retry
  index/    # backend plugins + vector query
  retrieval/ # retrieval result mapping + citations
  answer/   # grounded answer generation
  eval/     # demo evaluation runner
  server/   # FastAPI demo app
  manifest/ # RAGOps build manifests
  diff/     # RAGOps build comparisons
  report/   # RAGOps quality reports
  settings.py
  cli.py
tests/

Skip the embeddings artifact for remote-only deployments:

slimx-rag run --kb-dir examples/insurance_demo/knowledge-base --out-dir ./output --no-embeddings-out

Retrieve and answer

slimx-rag retrieve --out-dir ./output --q "What does the corpus say about edge AI?"
slimx-rag ask --out-dir ./output --q "What does the corpus say about edge AI?"

Use a real SlimX model for polished demos:

export OPENAI_API_KEY="..."
slimx-rag ask --out-dir ./output --model openai:gpt-4.1-mini --q "Compare SlimX Core and SlimX-RAG."

Run the research demo

uv sync --extra demo
uv pip install -e ../slimx
ollama pull llama3.2:3b
uv run slimx-rag run --kb-dir examples/research_demo/knowledge-base --out-dir output
OLLAMA_BASE_URL=http://localhost:11434 uv run slimx-rag ask --out-dir output --q "What is SlimX.ai's strongest business wedge for research organizations?" --model ollama:llama3.2:3b --timeout 300 --max-tokens 220 --max-context-chars 2500 --k 2
uv run slimx-rag eval --out-dir output --dataset examples/research_demo/eval/questions.jsonl --out output/eval_report.md
SLIMX_LLM_MODEL=ollama:llama3.2:3b SLIMX_LLM_TIMEOUT=300 SLIMX_LLM_MAX_TOKENS=220 SLIMX_MAX_CONTEXT_CHARS=2500 OLLAMA_BASE_URL=http://localhost:11434 uv run slimx-rag serve --host 127.0.0.1 --port 8080

Open http://127.0.0.1:8080.

Use --model fake:grounded when you need a deterministic no-network smoke test. If Ollama times out on the first request, warm it with ollama run llama3.2:3b "Say ready" and retry with a larger --timeout, smaller --max-tokens, smaller --max-context-chars, or lower --k.

Project details


Download files

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

Source Distribution

slimx_rag-0.2.6.tar.gz (209.1 kB view details)

Uploaded Source

Built Distribution

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

slimx_rag-0.2.6-py3-none-any.whl (107.8 kB view details)

Uploaded Python 3

File details

Details for the file slimx_rag-0.2.6.tar.gz.

File metadata

  • Download URL: slimx_rag-0.2.6.tar.gz
  • Upload date:
  • Size: 209.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for slimx_rag-0.2.6.tar.gz
Algorithm Hash digest
SHA256 1477497a4dcf07425fc18aeaeb9329ddd7083bbccdc9f79b4ee2c0dca0ea9e73
MD5 813d526b9f668df072f4833df81647ae
BLAKE2b-256 69a857749ac00cc81482a552f685910b41d97bc2697a2c081f4292b3b5c31558

See more details on using hashes here.

File details

Details for the file slimx_rag-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: slimx_rag-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 107.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for slimx_rag-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 bddf635794c2e339b5a3c23e5e027892f2b81495e0ad8f75152cf2a557b46e57
MD5 aa6a8300695642c28ecd069d0b52ebca
BLAKE2b-256 844d0e5eb70835fdaa9c490181cb2a0292971f984556998eda01ad9221ea2cb0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page