Skip to main content

RAG system for querying Obsidian notes using LangGraph and local LLMs

Project description

ObsidianRAG Backend

Python backend providing RAG (Retrieval-Augmented Generation) capabilities for Obsidian vaults.

PyPI Python Tests


๐Ÿš€ Installation

As End User

# With pip
pip install obsidianrag

# With pipx (recommended - isolated environment)
pipx install obsidianrag

# With uv (fastest)
uv tool install obsidianrag

As Developer

git clone https://github.com/Vasallo94/ObsidianRAG.git
cd ObsidianRAG/backend
uv sync

๐Ÿ“– Usage

CLI Commands

Start Server

# Serve with auto-detected vault
obsidianrag serve --vault /path/to/vault

# Custom port and model
obsidianrag serve --vault ~/notes --port 9000 --model qwen2.5

Index Vault

# Full rebuild
obsidianrag index --vault /path/to/vault --rebuild

# Incremental (only changed notes)
obsidianrag index --vault /path/to/vault

Check Status

obsidianrag status --vault /path/to/vault

Ask Question (CLI)

obsidianrag ask --vault /path/to/vault "What notes do I have about Python?"

๐Ÿ”Œ API

Start Server

from obsidianrag.api.server import run_server

run_server(vault_path="/path/to/vault", host="0.0.0.0", port=8000)

Endpoints

POST /ask

Ask a question about your notes.

Request:

{
  "text": "What notes do I have about Python?",
  "session_id": "optional-session-id"
}

Response:

{
  "question": "What notes do I have about Python?",
  "result": "According to your notes...",
  "sources": [
    {
      "source": "Programming/Python.md",
      "score": 0.92,
      "retrieval_type": "retrieved"
    }
  ],
  "text_blocks": ["..."],
  "process_time": 2.5,
  "session_id": "abc123"
}

POST /ask/stream

Same as /ask but streams response via Server-Sent Events (SSE).

Events:

  • start - Request started
  • status - Progress update
  • retrieve_complete - Documents retrieved
  • token - LLM token (streamed)
  • answer - Final answer
  • done - Stream complete
  • error - Error occurred

GET /health

Check server status.

Response:

{
  "status": "ok",
  "model": "gemma3",
  "embedding_provider": "huggingface",
  "embedding_model": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
  "db_ready": true
}

GET /stats

Get vault statistics.

Response:

{
  "total_notes": 150,
  "total_chunks": 450,
  "total_words": 25000,
  "total_chars": 150000,
  "avg_words_per_chunk": 55,
  "folders": 12,
  "internal_links": 350,
  "vault_path": "MyVault"
}

POST /rebuild_db

Force full database rebuild.


โš™๏ธ Configuration

Environment Variables

Create ~/.config/obsidianrag/.env:

# LLM
LLM_MODEL=gemma3
OLLAMA_BASE_URL=http://localhost:11434

# Embeddings
EMBEDDING_PROVIDER=huggingface  # or 'ollama'
EMBEDDING_MODEL=sentence-transformers/paraphrase-multilingual-mpnet-base-v2
OLLAMA_EMBEDDING_MODEL=nomic-embed-text

# Reranker
USE_RERANKER=true
RERANKER_MODEL=BAAI/bge-reranker-v2-m3
RERANKER_TOP_N=6

# Retrieval
CHUNK_SIZE=1500
CHUNK_OVERLAP=300
RETRIEVAL_K=12
BM25_K=5
BM25_WEIGHT=0.4
VECTOR_WEIGHT=0.6

# API
API_HOST=127.0.0.1
API_PORT=8000
CORS_ORIGINS=["http://localhost:3000", "app://obsidian.md"]

Programmatic Configuration

from obsidianrag.config import Settings, configure_from_vault

# Auto-configure from vault
configure_from_vault("/path/to/vault")

# Manual configuration
settings = Settings(
    obsidian_path="/path/to/vault",
    llm_model="qwen2.5",
    use_reranker=True,
    retrieval_k=15
)

๐Ÿ—๏ธ Architecture

Core Components

obsidianrag/
โ”œโ”€โ”€ api/
โ”‚   โ””โ”€โ”€ server.py         # FastAPI server
โ”œโ”€โ”€ cli/
โ”‚   โ””โ”€โ”€ main.py           # CLI commands (Typer)
โ”œโ”€โ”€ core/
โ”‚   โ”œโ”€โ”€ qa_agent.py       # LangGraph RAG agent
โ”‚   โ”œโ”€โ”€ qa_service.py     # Hybrid retriever + reranker
โ”‚   โ”œโ”€โ”€ db_service.py     # ChromaDB + indexing
โ”‚   โ””โ”€โ”€ metadata_tracker.py  # Change detection
โ”œโ”€โ”€ config/
โ”‚   โ””โ”€โ”€ __init__.py       # Pydantic settings
โ””โ”€โ”€ utils/
    โ””โ”€โ”€ logger.py         # Logging

RAG Pipeline

LangGraph Agent (qa_agent.py):

# Two-node graph: retrieve โ†’ generate
graph = StateGraph(AgentState)
graph.add_node("retrieve", retrieve_node)
graph.add_node("generate", generate_node)
graph.add_edge("retrieve", "generate")

Hybrid Retriever (qa_service.py):

  1. Vector search (ChromaDB)
  2. BM25 search
  3. Ensemble (weighted 0.6/0.4)
  4. CrossEncoder reranking
  5. GraphRAG link expansion

Database Service (db_service.py):

  • ChromaDB persistence
  • Incremental indexing (only changed notes)
  • Metadata tracking
  • Link extraction from [[wikilinks]]

๐Ÿงช Testing

# All tests
uv run pytest

# Unit tests only (no integration, no slow)
uv run pytest -m "not integration and not slow"

# With coverage
uv run pytest --cov=obsidianrag --cov-report=html

Test Structure:

  • tests/test_cli.py - CLI commands (14 tests)
  • tests/test_server.py - API endpoints (14 tests)
  • tests/test_qa_agent.py - LangGraph agent (17 tests)
  • tests/test_db_service.py - Database (16 tests)
  • tests/test_integration.py - E2E flows (16 tests)

Total: 77 tests, 42% coverage


๐Ÿ”ง Development

Setup

# Clone repo
git clone https://github.com/Vasallo94/ObsidianRAG.git
cd ObsidianRAG/backend

# Install with dev dependencies
uv sync

# Run tests
uv run pytest

# Lint and format
uv run ruff check obsidianrag/ tests/
uv run ruff format obsidianrag/ tests/

Project Structure

backend/
โ”œโ”€โ”€ obsidianrag/          # Main package
โ”œโ”€โ”€ tests/                # Tests
โ”œโ”€โ”€ pyproject.toml        # Package metadata + dependencies
โ”œโ”€โ”€ uv.lock               # Lock file
โ””โ”€โ”€ pytest.ini            # Pytest configuration

๐Ÿ“„ License

MIT License - see LICENSE


๐Ÿค Contributing

See CONTRIBUTING.md

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

obsidianrag-3.0.2.tar.gz (29.0 kB view details)

Uploaded Source

Built Distribution

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

obsidianrag-3.0.2-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file obsidianrag-3.0.2.tar.gz.

File metadata

  • Download URL: obsidianrag-3.0.2.tar.gz
  • Upload date:
  • Size: 29.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for obsidianrag-3.0.2.tar.gz
Algorithm Hash digest
SHA256 7bc1a0c25e0caed796afd1612f36b9266ee953fc820022921dbfa976303ef2eb
MD5 093af9db362828996d51c63a9eb1fe76
BLAKE2b-256 862a308af534d86a17df2ddb8229854637e2249aa2cc93e7bf06039b0c6bdd06

See more details on using hashes here.

File details

Details for the file obsidianrag-3.0.2-py3-none-any.whl.

File metadata

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

File hashes

Hashes for obsidianrag-3.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 80b30a36a09b870694e95e849fbdd42198be0891365ef6ca5f46966f0262f895
MD5 5244bea2389d9317f93751f83fe874cb
BLAKE2b-256 b39c95198be65bad42d3dab6687db992a65d77613adfd342930ecfd9597b0865

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