Skip to content

Getting started

Requirements

  • Python 3.11
  • Enough disk for your JSONL sources plus the derived dense and sparse indexes. Read the size from the bundle or local data directory rather than a copied estimate.
  • A generation endpoint — any configured OpenAI- or Anthropic-compatible API. That can be a cloud provider, a free-tier proxy, or a fully local model server. Retrieval works with no LLM at all.

CPU is enough: embeddings (bge-small) and the cross-encoder reranker both run on CPU.

Install

pip install -r requirements.txt
cp .env.example .env      # add your generation key, or point at a local server

Configure

All tunables live in config.yaml (copy config.example.yaml if you're starting fresh). Set the path to your documents, then select a generation backend by name:

parser:
  vault_path: "/path/to/your/documents"   # the folder to index

generation:
  provider: ollama          # any name from the `providers:` registry
  model: "llama3.1:8b"      # what that endpoint actually serves

Secrets stay in .env (gitignored); everything else is in config.yaml, which the system can also rewrite in place (comment-preserving) when you change defaults live. After starting the query service, GET /providers shows the active registry entry and whether each configured key is present and type-compatible, without returning values.

The shipped registry already covers a spread of hosted endpoints and the common local runtimes (Ollama, LM Studio, KoboldCPP), so in most cases selecting a backend is one word plus one environment variable. Add your own by copying any entry: kind is the wire protocol (openai or anthropic), not the vendor.

Model ids move

The model in each shipped registry entry was that endpoint's sensible default when the file was written — treat it as a starting point. Every endpoint lists what it actually serves at GET <base_url>/models, and the console copies an entry's model into generation.model when you activate it.

Some subscriptions are not API access

A consumer chat subscription usually grants no API quota. Where a vendor offers both, the two credentials are different products and are not interchangeable — a provider entry can declare api_key_prefix so the wrong key type is rejected up front instead of billing separately by surprise.

Fully local

To run with no cloud dependency at all — local embeddings plus a local model server for generation — follow RUN_LOCAL.md. Point one of the local provider entries at your server and select it with generation.provider.

Build the indexes

# 1. Parse markdown notes -> data/chunks.jsonl
python -m src.ingestion.obsidian_parser "path/to/your/documents" -o data/chunks.jsonl

# 2. Build the dense (ChromaDB) + sparse (bm25s) indexes
python main.py index

Add other source families and append them (each writes its own JSONL, and index --append rebuilds the sparse half automatically):

python main.py ingest-pdfs                        # -> data/pdf_chunks.jsonl
python main.py ingest-notebooks                   # -> data/ipynb_chunks.jsonl
# a SCOPED run replaces whatever file it writes, so it gets its own output:
python main.py ingest-code --include-path "src" --output data/src_code_chunks.jsonl

python main.py index --append data/pdf_chunks.jsonl
python main.py index --append data/ipynb_chunks.jsonl
python main.py index --append data/src_code_chunks.jsonl

Chunking strategy

Pass --chunking heading (default) for structured documents or --chunking fixed for OCR walls of text. See Architecture.

Ask your first question

python main.py query "What does our incident response process require?"
python main.py chat        # interactive REPL

Serve

# Warm query API (agents / scripts / bots)
python -m uvicorn serve_api:app --host 127.0.0.1 --port 8051

# Corpus Ledger console (visual management + Query tab)
python -m uvicorn manage_api:app --host 127.0.0.1 --port 8052

Then open http://127.0.0.1:8052 for the console, or POST to :8051 from code. Next: Usage.

Prefer containers?

The same stack runs in containers: from a source clone with the Compose files in deploy/docker/, or from a release archive with no local Python setup at all. See Docker deployment.