Skip to content

TriBridRAG Documentation

  • Tri-brid Retrieval


    Parallel Vector (pgvector), Sparse (PostgreSQL FTS/BM25), and Graph (Neo4j) search fused with configurable strategies.

  • Pydantic Is The Law


    All configuration and API shapes come from server/models/tribrid_config_model.py. TypeScript types are generated — never hand-written.

  • PostgreSQL Backbone


    Chunk storage, embeddings, pgvector indexing, and FTS live in one database.

  • Knowledge Graph


    Neo4j stores entities and relationships; graph traversal augments retrieval for cross-file context.

  • API-First


    FastAPI endpoints for indexing, retrieval, config, models, graph, health, and metrics.

  • Operational Safety


    Field constraints, health/readiness, Prometheus metrics, and cost-aware models.

Get started Configuration API

Read This First

TriBridRAG is strictly Pydantic-first. If a field or feature is not in server/models/tribrid_config_model.py, it does not exist. Add it there, regenerate TypeScript types, then build the rest.

Terminology — corpus vs repo_id

The API still accepts repo_id for legacy reasons. Treat it as the corpus identifier. Pydantic models use AliasChoices("repo_id", "corpus_id") and serialize as corpus_id.

Security

Keep .env out of version control. Restrict database access. Use strong passwords for PostgreSQL and Neo4j. Rotate API keys regularly.

What TriBridRAG Does

Feature Description Status
Vector Search Dense similarity via pgvector in PostgreSQL ✅ Active
Sparse Search PostgreSQL FTS/BM25 for exact terms, identifiers ✅ Active
Graph Search Neo4j traversal to follow entities/relations ✅ Active
Fusion Weighted/reciprocal-rank fusion of sources ✅ Active
Reranker Optional cross-encoder reranking ✅ Active

End-to-End Retrieval Flow

flowchart LR
    Q["Query"] --> V["Vector Search\npgvector"]
    Q --> S["Sparse Search\nPostgreSQL FTS/BM25"]
    Q --> G["Graph Search\nNeo4j"]
    V --> F["Fusion Layer"]
    S --> F
    G --> F
    F --> R["Optional Reranker"]
    R --> O["Results"]
    F --> O
  • Configure environment (.env)
  • Launch services with Docker Compose
  • Regenerate TypeScript types from Pydantic
  • Index a corpus
  • Search via API
  • Tune fusion weights and confidence thresholds
  • Enable reranking if needed

Use Ctrl+C to stop local uvicorn or Docker Tail sessions.

import httpx, subprocess

BASE = "http://localhost:8000"

# 1) Generate TS types from Pydantic (required for UI) (1)
subprocess.check_call(["uv", "run", "scripts/generate_types.py"])  # (1)

# 2) Trigger indexing of a corpus (2)
req = {
    "corpus_id": "tribrid",  # repo_id alias is also accepted (3)
    "repo_path": "/path/to/your/codebase",
    "force_reindex": False,
}
httpx.post(f"{BASE}/index", json=req).raise_for_status()

# 3) Poll status (4)
status = httpx.get(f"{BASE}/index/status", params={"corpus_id": "tribrid"}).json()
print(status)

# 4) Search (parallel vector/sparse/graph -> fusion -> optional rerank) (5)
payload = {
    "corpus_id": "tribrid",
    "query": "How does the chunker split Python files?",
    "top_k": 8,
}
res = httpx.post(f"{BASE}/search", json=payload).json()
for m in res.get("matches", []):
    print(m["file_path"], m["score"])  # fused score
BASE=http://localhost:8000

# (1) Types are generated locally via Python; no curl equivalent

# (2) Start indexing
curl -sS -X POST "$BASE/index" \ 
  -H 'Content-Type: application/json' \ 
  -d '{
    "corpus_id": "tribrid",
    "repo_path": "/path/to/your/codebase",
    "force_reindex": false
  }'

# (4) Status
curl -sS "$BASE/index/status?corpus_id=tribrid" | jq .

# (5) Search
curl -sS -X POST "$BASE/search" \ 
  -H 'Content-Type: application/json' \ 
  -d '{
    "corpus_id": "tribrid",
    "query": "How does the chunker split Python files?",
    "top_k": 8
  }' | jq '.matches[] | {file_path, score}'
// (1) Ensure ./web/src/types/generated.ts exists (generated by Python)
import type { IndexRequest, SearchRequest, SearchResponse } from "./web/src/types/generated";

async function indexAndSearch() {
  const base = "http://localhost:8000";

  const indexReq: IndexRequest = {
    corpus_id: "tribrid", // repo_id alias is also accepted server-side (3)
    repo_path: "/path/to/your/codebase",
    force_reindex: false,
  };
  await fetch(`${base}/index`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(indexReq),
  }); // (2)

  const searchReq: SearchRequest = {
    corpus_id: "tribrid",
    query: "chunker split Python",
    top_k: 8,
  } as any; // depending on generated type

  const r = await fetch(`${base}/search`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(searchReq),
  });
  const data: SearchResponse = await r.json(); // (5)
  console.log(data.matches.map(m => [m.file_path, m.score]));
}
  1. Generate UI types from Pydantic models — this is the contract for the frontend
  2. Start indexing your corpus folder
  3. Inputs accept repo_id but serialize as corpus_id
  4. Poll index status to show progress in UI
  5. Search runs vector/sparse/graph in parallel, fuses, then optionally reranks

One Config To Rule Them All

Every behavior above is configured by TriBridConfig. Adjust fusion weights, hop limits, Top-K, and reranking in the config and the system adapts immediately.

Architecture Overview

flowchart TB
    subgraph Client
      U["User / UI / API Client"]
    end

    U --> A["FastAPI"]
    A --> V["VectorRetriever\nPostgres+pgvector"]
    A --> S["SparseRetriever\nPostgres FTS/BM25"]
    A --> G["GraphRetriever\nNeo4j"]
    V --> F["Fusion"]
    S --> F
    G --> F
    F --> R["Reranker (optional)"]
    R --> O["Final Results"]
    F --> O

    subgraph Storage
      P[("PostgreSQL")]
      N[("Neo4j")]
    end

    V <--> P
    S <--> P
    G <--> N
Advanced Topics
  • Fusion math: supports weighted linear combination and Reciprocal Rank Fusion with configurable rrf_k.
  • Retrieval cache: cache keys include corpus_id, query, and a hash of the retrieval config segment for correctness.
  • Failure isolation: vector, sparse, and graph legs are resilient; a failure in one path degrades gracefully without crashing the whole search.