# cartog-rag

Semantic search and RAG pipeline for cartog.

## Overview

Provides embedding-based search on top of the cartog graph database. Combines keyword search (FTS5/BM25) with vector similarity (sqlite-vec) and cross-encoder reranking for high-quality results.

Indexes both **code symbols** (functions, classes, methods) and **Markdown documents** (chunked by heading). Documentation and code are searchable side-by-side with the same hybrid pipeline.

## How it works

### Embedding providers

Configurable via `.cartog.toml` `[embedding]` section. Supports pluggable providers:

| Provider | Feature flag | Models | Notes |
|----------|-------------|--------|-------|
| **local** (default) | `provider-local` | Any fastembed built-in (BGE, all-MiniLM, nomic, etc.) | ONNX Runtime via fastembed, auto-downloaded from HuggingFace |
| **ollama** | `provider-ollama` | Any Ollama model (nomic-embed-text, mxbai-embed-large, etc.) | HTTP client, auto-detects dimension |
| **openai** | `provider-openai` | Any OpenAI-compatible `/v1/embeddings` model (OpenAI, Mistral, Voyage, Jina, …) | HTTP client; switch vendors via `base_url`, API key from the env var named by `api_key_env` |

**Reranker**: jina-reranker-v1-turbo-en (default, ~150MB) — cross-encoder that scores (query, document) pairs jointly (optional, local only). Configurable via `[reranker] model` (any fastembed reranker repo path); unset uses the default.

Local models cached in `~/.cache/cartog/models/` (respects `FASTEMBED_CACHE_DIR` and `XDG_CACHE_HOME`). Ollama models are managed by the Ollama server.

```toml
# .cartog.toml — example with Ollama
[embedding]
provider = "ollama"
model = "nomic-embed-text"
# dimension = 768  # auto-detected if omitted

# Local provider only: asymmetric model prefixes
# [embedding.local]
# query_prefix = "search_query: "
# document_prefix = "search_document: "
```

### Hybrid search pipeline

1. **FTS5 keyword search** — BM25 ranking over symbol names and source content
2. **Vector KNN search** — cosine similarity on configurable-dimension embeddings via sqlite-vec
3. **RRF merge** — Reciprocal Rank Fusion (k=60) combines both ranked lists
4. **Hydration** — load symbol metadata and source content from the database
5. **Cross-encoder reranking** — reranker scores top-50 candidates (if model available)
6. **Final sort** — by rerank score (if present), else RRF score, with in-degree tiebreaker

Over-retrieves `(limit * 3).max(20)` candidates from each source to improve fusion quality before applying the final limit.

### Provider lifecycle

Providers are created per-command invocation via `create_embedding_provider(config)`. The caller passes an `EmbeddingProviderConfig` (from `.cartog.toml`) and receives a boxed `EmbeddingProvider` trait object. Reranker providers are created with `create_reranker_provider(reranker_provider, model, intra_threads)`, which returns `None` when re-ranking is disabled, the model is unavailable, or the `provider-local` feature is off.

The Ollama provider maps transport failures to actionable errors instead of raw reqwest chains: a refused connection points at `ollama serve` and `[embedding.ollama].base_url`, and an HTTP 404 from `/api/embed` tells the user to run `ollama pull <model>`. The original error is preserved as the cause.

## Public API

| Export | Description |
|--------|-------------|
| `create_embedding_provider()` | Create a provider from config |
| `create_reranker_provider()` | Create a reranker from config (provider "local"/"none" + a model repo path) |
| `EmbeddingProviderConfig` | Configuration for provider selection |
| `provider::EmbeddingProvider` | Trait for embedding backends |
| `provider::RerankerProvider` | Trait for reranker backends |
| `search::hybrid_search()` | Run the full hybrid search pipeline |
| `indexer::index_embeddings()` | Embed symbols and write vectors to DB. Optional `progress: Option<ProgressCallback>` fires per batch (`Preparing`, `Embedding{processed,total}`, `Storing`) and `cancel: Option<CancelProbe>` aborts cooperatively; pass `None` for no-op. |
| `indexer::ProgressUpdate` / `indexer::ProgressCallback` / `indexer::CancelProbe` | Phase enum + `Fn` trait-object type aliases for in-flight reporting and cancellation (transport-agnostic) |
| `setup::download_model()` | Download the embedding model (requires the `provider-local` feature) |
| `EMBEDDING_DIM` | Default vector dimension (384) |

## Crate dependencies

`cartog-core`, `cartog-db`
