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.
๐ 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 startedstatus- Progress updateretrieve_complete- Documents retrievedtoken- LLM token (streamed)answer- Final answerdone- Stream completeerror- 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):
- Vector search (ChromaDB)
- BM25 search
- Ensemble (weighted 0.6/0.4)
- CrossEncoder reranking
- 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bc1a0c25e0caed796afd1612f36b9266ee953fc820022921dbfa976303ef2eb
|
|
| MD5 |
093af9db362828996d51c63a9eb1fe76
|
|
| BLAKE2b-256 |
862a308af534d86a17df2ddb8229854637e2249aa2cc93e7bf06039b0c6bdd06
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80b30a36a09b870694e95e849fbdd42198be0891365ef6ca5f46966f0262f895
|
|
| MD5 |
5244bea2389d9317f93751f83fe874cb
|
|
| BLAKE2b-256 |
b39c95198be65bad42d3dab6687db992a65d77613adfd342930ecfd9597b0865
|