This project has been renamed to ragweld. The internal codebase still references "tribrid" in many places — config keys, interfaces, variable names — and that will stay as-is. The product name, repo, and website are now ragweld.
One complete, self-hosted AI platform for retrieval and agent systems
Model routing, multimodal indexing and retrieval, evals, training, tracing, and operations—assembled into one opinionated workbench instead of a dozen disconnected tools.
Building a serious AI stack usually means researching overlapping model gateways, document pipelines, vector and graph stores, evaluation systems, training tools, trace platforms, and observability products—and then owning every integration between them. Ragweld makes those choices, wires the components together, and exposes the result as one platform with shared configuration, provenance, health, and operator workflows.
It is built for two closely related users:
- AI consultancies and technical teams serving SMBs that need a complete, repeatable platform without rebuilding the stack for every deployment.
- RAG and agent power users who want the full retrieval, evaluation, training, tracing, cost, and infrastructure picture without connecting every subsystem themselves.
The underlying open-source systems remain inspectable and replaceable, but the product value is the integration: sane defaults, one operational surface, and a quality loop that connects real queries and traces to evals, training, promotion, and rollback.
Local main is the canonical development line and origin/main is its publication target. The OSS-composition work is a formal replacement program for the broken bespoke platform layers, not a parallel experimental branch.
- No fallbacks
- No legacy compatibility shims
- No transition-period dual paths
- No backend-only swaps that leave operator UI stale
- If a subsystem is replaced, the touched backend/UI/docs/tests/instructions must move to the new path together
- Inference:
vLLM - Gateway/routing:
LiteLLM - Orchestration:
Flyte - Retrieval/indexing:
Haystack + Docling + Qdrant - Graph parity:
Neo4j - Training execution:
Unsloth - Runs/evals/regressions:
MLflow + Ragas + Promptfoo - Eval drilldown substrate:
Langfuse - Observability:
OpenTelemetry + Grafana Alloy + Tempo + Loki + Mimir + Pyroscope + Faro - Frontend shell/workbench target:
Dockview + react-resizable-panels + TanStack Query + assistant-ui + shadcn/ui + Radix + xterm + Monaco
- Workspace shell and dock/splits experience
- Embedded Grafana and operator-console feel
- Training Center as a first-class in-product surface
- Eval analysis and drilldown as a first-class in-product surface
- Graph parity surfaces during the migration
Chat is explicitly not protected as an implementation. It should be rebuilt inside the ragweld shell on stronger OSS foundations.
- Generation now has one application boundary:
Ragweld API -> LiteLLM -> vLLM. Upstream paid credentials and routes remain private to LiteLLM; the application sees authenticated aliases only. - Retrieval/indexing runs on the
Docling + Haystack + Qdrantlane: Docling extracts rich documents, every corpus's dense and sparse vectors live in a promoted Qdrant generation, Postgres keeps chunk rows as control/state, and Neo4j keeps the graph leg. - Online observability now has a hard-cut
OTel + Langfuse + Tempo + Alloycontrol surface, canonical trace metadata, cost attribution, and workbench visibility.
- Training execution still runs on the MLX-native backend: Flyte orchestrates Learning Agent runs and MLflow tracks them, but
Unslothneeds a CUDA host. - Eval analysis/drilldown still needs its full
Langfuse + MLflow + Ragas/Promptfoosubstrate. - Chat is still hand-rolled and has not yet been rebuilt on
assistant-ui. - Large parts of the repo still describe the mainline implementation below; treat the sections in this README as current-state context unless the fork status above says otherwise.
Single-method RAG systems fail in predictable ways:
- Vector search misses exact identifiers, function names, and error codes
- Keyword/BM25 search fails on conceptual queries and paraphrases
- Neither understands code relationships—what calls what, what imports what
ragweld runs three retrieval methods in parallel, fuses their results, and optionally reranks:
┌─────────────────────┐
│ Vector Search │
│ (Qdrant) │
│ Semantic similarity│
┌────►│ "auth flow" → │────┐
│ │ "token exchange" │ │
│ └─────────────────────┘ │
│ │
┌──────────────┐ │ ┌─────────────────────┐ │ ┌─────────────┐ ┌──────────────┐
│ │ │ │ Sparse Search │ │ │ │ │ │
│ Query │──────────────┼────►│ (PostgreSQL FTS) │────┼────►│ Fusion │────►│ Reranker │────► Results
│ │ │ │ BM25 ranking │ │ │ (RRF / │ │ (optional) │
└──────────────┘ │ │ Exact matches │ │ │ Weighted) │ │ │
│ └─────────────────────┘ │ └─────────────┘ └──────────────┘
│ │
│ ┌─────────────────────┐ │
│ │ Graph Search │ │
└────►│ (Neo4j) │────┘
│ Entity traversal │
│ "what calls this?"│
└─────────────────────┘
Each search method compensates for the others' weaknesses. The result: dramatically better recall across query types.
- Versioned source-of-truth config: Pydantic-backed config is the law, with generated types and UI/API surfaces derived from it
- Versioned prompt behavior: system prompts and config-driven runtime behavior are explicit, reviewable repo artifacts
- Quality-loop lineage bundles: benchmark, eval, synthetic, training, and promotion flows attach immutable versions into corpus-scoped bundles with operator aliases
- Executable specs: machine-checkable intent lives in
spec/and is meant to be updated with product behavior - Manifest-backed training artifacts: training and promotion flows are anchored in explicit artifacts and manifests, not hand-waved state
- Repo-local system of record: agent/operator knowledge stays versioned in-repo under
docs/ - Provenance-minded workflows: eval, training, tracing, and synthetic flows are designed for traceability, without claiming full end-to-end DSV governance
- Synthetic Data Lab: recipe-based generation for eval datasets, semantic cards, keywords, triplets, and autotune patches
- Dual Training Studios: trainable learning reranker and trainable in-product agent model, with checkpoints, promote/rollback, and run telemetry
- Evaluation Workbench: run management, drilldowns, and diffs across retrieval, indexing, model, and routing changes
- Tracing + Observability: branch target is
OTel + Langfuse + Tempo + Alloy + Grafana; the online request slice is already wired end-to-end with in-product status, trace deep links, and cost attribution - Semantic Cache + Recall Gates: token-cost control and smart memory retrieval policy in the chat path
- Gateway Aliases + Model Catalog: authenticated LiteLLM runtime aliases alongside a separate pricing/candidate catalog
- Vector Search: dense chunk vectors in Qdrant (HNSW, cosine), one promoted generation per corpus
- Sparse Search: IDF-modified BM25 sparse vectors (fastembed
Qdrant/bm25) stored in the same Qdrant generation - Graph Search: Neo4j knowledge graph with entity extraction and relationship traversal
- Fusion: Reciprocal Rank Fusion (RRF) or configurable weighted scoring
- Reranking: Cloud APIs (Cohere/Voyage/Jina) or Qwen3 LoRA learning rerankers (MLX)
- Backend: FastAPI with async support, comprehensive API
- Frontend: React + TypeScript + Zustand, fully typed from Pydantic
- Configuration: 500+ tunable parameters, all via UI or API
- Observability: canonical request tracing now centers on OpenTelemetry, Langfuse, Tempo, Alloy, Grafana, and the workbench trace cache bridge
- Primary contract: FastAPI endpoints under
/api/*are the canonical production interface - Channel aliases: optional LiteLLM model aliases for API, chat, CLI, and MCP channels through one gateway
- Ops surface: indexing, retrieval, evals, training, tracing, and observability are all API-addressable
- Embedded MCP Server: Streamable HTTP transport mounted at
/mcp - Three MCP Tools:
search,answer,list_corpora - Claude Desktop / IDE Ready: Connect any MCP-compatible client directly to ragweld
- Stateless HTTP Mode: No session management required (recommended for most use cases)
- Automatic entity extraction (functions, classes, modules, variables)
- Relationship mapping (calls, imports, inherits, contains, references)
- Community detection (Louvain, Label Propagation)
- Graph inspection via RAG → Graph (UI), Neo4j Browser, and
/api/graph/*endpoints
- Per-Request Traces: Full request metadata for chat/search/answer, including canonical trace ids, correlation ids, route summaries, external links, and cost summaries
- Debug Footer: Inline debug metadata showing confidence, fusion method, retrieval leg counts
- Langfuse + Tempo Deep Links: Workbench traces can link directly into external drilldown tools when configured
- Loki Integration: Stream logs directly in the Chat UI via Loki proxy endpoints
- Trace Cache Bridge: Configurable local retention while the workbench migrates toward fully canonical observability APIs
- Built-in evaluation framework with eval datasets
- Detailed Cost Breakdowns: Per-request costs for generation, embeddings, and reranking
- Model comparison tools with accurate pricing from
data/models.json - Retrieval quality metrics (MRR, Recall@K, NDCG)
ragweld measures real pipeline latency and throughput via Prometheus + Grafana (see dashboards tribrid-overview and tribrid-rag-metrics). For reproducible local benchmarking (index + search), use the built-in benchmark runner.
Prereqs:
docker compose up -d postgres neo4j
# If using the docker-compose defaults:
export NEO4J_PASSWORD=passwordRun:
uv run scripts/benchmark_perf.py --corpus-id ragweld --corpus-path . --force-reindex --iterations 5 --warmup 1This prints Markdown + JSON summary you can paste into docs/PRs.
Generated on 2026-02-01 with the command above (vector+sparse+graph enabled, final_k=10).
| Operation | Performance | Notes |
|---|---|---|
| Indexing | 419.0s | 497 files, 8,753 chunks, 702,647 tokens |
| Search (tri-brid) | p50 459ms, p95 487ms | 25 calls (5 queries × 5 iters), ~1.62 QPS |
Per-query (ms):
| Query | p50 | p95 | mean |
|---|---|---|---|
| authentication flow | 373.5 | 758.5 | 449.6 |
| prometheus metrics endpoint /metrics | 462.5 | 484.9 | 466.5 |
| neo4j graph retrieval mode | 461.6 | 487.1 | 467.5 |
| where is /api/search implemented | 449.4 | 458.7 | 450.0 |
| fusion rrf_k parameter | 471.0 | 483.0 | 465.9 |
Treat the mainline status section above as the architecture truth for modernization decisions. Implementation details below may still describe older subsystems that have not been fully replaced yet.
- Docker & Docker Compose
- Python 3.11+ with uv
- Node.js 18+
- API keys for your preferred embedding provider (OpenAI, Voyage, etc.)
git clone https://github.com/DMontgomery40/ragweld.git
cd ragweld
cp .env.example .envEdit .env with your API keys:
OPENAI_API_KEY=sk-...
# or
VOYAGE_API_KEY=pa-...docker compose up -d postgres neo4jBy default, Docker volumes are stored alongside the repo. To store DB data outside the repo (recommended for real corpora),
set TRIBRID_DB_DIR (wired in docker-compose.yml):
# Example (macOS)
export TRIBRID_DB_DIR="/Users/davidmontgomery/ragweld-db"
docker compose up -d postgres neo4jThis starts:
- PostgreSQL for corpus control/state rows and caches (port 5432)
- Qdrant vector store for dense + sparse chunk vectors (port 56333)
- Neo4j graph database (ports 7474, 7687)
docker compose -f docker-compose.yml -f infra/docker-compose.observability.yml up -d grafana tempo alloyRecommended matching config values on this branch:
tracing.tracing_mode=otel or otel_langfuse
tracing.otlp_endpoint=http://localhost:54320/v1/traces
tracing.alloy_base_url=http://localhost:52345
tracing.tempo_base_url=http://localhost:53200
ui.grafana_base_url=http://localhost:3301
Reference:
infra/docker-compose.observability.ymlinfra/alloy/config.alloyinfra/tempo.yamldocs/references/observability-online-slice.md
uv sync
uv run uvicorn server.main:app --reload --port 58012Notes:
- The backend will auto-load repo-root
.envon startup (dev convenience). - If you change
.envwhile the backend is running, you must restart the backend for changes to take effect.
API available at http://localhost:58012 OpenAPI docs at http://localhost:58012/docs
cd web
npm install
npm run devFrom the repo root:
./start.shWith full observability stack (Prometheus + Grafana + Loki + Promtail):
./start.sh --with-observability
./start.sh --with-flyte # adds the Flyte control plane for Learning Agent orchestrationStop only Ragweld-owned host processes and containers while preserving all data volumes and the host-owned Colima VM:
./stop.shReset only the fixed ragweld Compose project's volumes after stopping it:
./stop.sh
./reset-data.sh --confirm=DELETE-RAGWELD-DATAThe reset command never deletes repo-local files, models, or Colima itself.
Observability URLs:
- Grafana: http://localhost:3301 (admin/admin)
- Prometheus: http://localhost:59090
- Loki: http://localhost:53100
UI available at http://localhost:55173
Via UI: Use the corpus switcher in the top navigation.
Via API:
# Create a corpus
curl -X POST "http://localhost:58012/api/repos" \
-H "Content-Type: application/json" \
-d '{
"name": "my-project",
"path": "/absolute/path/to/your/code"
}'
# Start indexing
curl -X POST "http://localhost:58012/api/index" \
-H "Content-Type: application/json" \
-d '{"repo_id": "my-project", "repo_path": "/path/to/my-project", "force_reindex": false}'
# Check progress
curl "http://localhost:58012/api/index/my-project/status"curl -X POST "http://localhost:58012/api/search" \
-H "Content-Type: application/json" \
-d '{
"query": "How does the authentication system work?",
"corpus_id": "my-project",
"top_k": 10
}'ragweld includes a built-in Model Context Protocol (MCP) server, allowing any MCP-compatible client (Claude Desktop, Cursor, VS Code extensions, custom agents) to use tri-brid retrieval directly.
| Tool | Description |
|---|---|
search |
Tri-brid search (vector + sparse + graph) returning ranked chunks |
answer |
RAG-powered answer generation with citations |
list_corpora |
List all available corpora for searching |
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"tribrid-rag": {
"url": "http://localhost:58012/mcp/"
}
}
}Restart Claude Desktop. You can now ask Claude to search your indexed codebases.
MCP settings are in tribrid_config.json under the mcp section:
| Setting | Default | Description |
|---|---|---|
enabled |
true |
Enable/disable the MCP server |
mount_path |
/mcp |
HTTP endpoint path |
stateless_http |
true |
Stateless mode (recommended) |
json_response |
true |
JSON responses (recommended) |
require_api_key |
false |
Require Bearer token auth |
default_top_k |
20 |
Default result count |
default_mode |
tribrid |
Default retrieval mode |
| Mode | Vector | Sparse | Graph | Use Case |
|---|---|---|---|---|
tribrid |
✓ | ✓ | ✓ | Best recall (default) |
dense_only |
✓ | - | - | Semantic queries only |
sparse_only |
- | ✓ | - | Exact keyword matching |
graph_only |
- | - | ✓ | Relationship traversal |
curl http://localhost:58012/api/mcp/statusragweld captures detailed per-request traces for debugging and development. Traces are stored in an in-memory ring buffer (no external dependencies).
- Automatic Capture: Every chat/search request generates a trace
- Event Timeline: Request → Retrieval → Fusion → Response events with timestamps
- Debug Footer: Inline metadata on every chat response showing:
- Confidence score (heuristic based on fusion method)
- Active retrieval legs (vector/sparse/graph)
- Fusion method and parameters (RRF k or weighted weights)
- Result counts per leg
- Run ID for correlation
| Endpoint | Description |
|---|---|
GET /api/traces/latest |
Get the most recent trace |
GET /api/traces/latest?run_id=... |
Get a specific trace by run ID |
GET /api/traces/latest?repo=... |
Get latest trace for a corpus |
| Setting | Default | Description |
|---|---|---|
tracing.tracing_enabled |
1 |
Enable trace capture |
tracing.tracing_mode |
local |
Trace mode (local, otel, otel_langfuse, off) |
tracing.otel_export_enabled |
1 |
Enable canonical OTLP trace export |
tracing.otlp_endpoint |
"" |
OTLP HTTP endpoint for OTel export |
tracing.otlp_headers |
"" |
Comma-separated OTLP headers (k=v) |
tracing.otel_service_name |
ragweld-api |
Service identity attached to emitted spans |
tracing.langfuse_enabled |
0 |
Enable Langfuse generation observations |
tracing.langfuse_base_url |
"" |
Langfuse deployment base URL |
tracing.langfuse_project |
ragweld |
Langfuse project label |
tracing.tempo_base_url |
"" |
Tempo or Grafana Explore base URL for trace deep links |
tracing.alloy_base_url |
"" |
Grafana Alloy collector base URL for readiness/status checks |
tracing.cost_tracking_enabled |
1 |
Enable online request cost attribution in traces |
tracing.trace_retention |
50 |
Max traces per corpus (ring buffer) |
tracing.trace_sampling_rate |
1.0 |
Sampling rate (1.0 = capture all) |
ui.chat_show_debug_footer |
1 |
Show debug footer under chat answers |
When running with the observability stack, ragweld proxies Loki queries for unified log viewing in the Chat UI.
| Endpoint | Description |
|---|---|
GET /api/loki/status |
Check Loki reachability |
GET /api/stream/loki/tail |
SSE stream of live logs |
The Chat tab includes a Trace & Logs panel that:
- Shows the trace timeline for the current/selected run
- Streams live logs from Loki filtered by service
- Correlates logs with trace events by timestamp
This section is optimized for running a real on-disk corpus end-to-end (Postgres + Neo4j), indexing it safely (gitignore-aware + size skips), and verifying graph retrieval is actually contributing results.
# Start the fixed Ragweld Compose project plus the host API/UI.
./start.sh
# Create a corpus from any real document directory, then exclude heavy build outputs.
curl -sS -X POST http://127.0.0.1:58012/api/repos -H 'Content-Type: application/json' -d '{"corpus_id":"example-corpus","name":"Example corpus","path":"/path/to/documents"}'
curl -sS -X PATCH http://127.0.0.1:58012/api/repos/example-corpus -H 'Content-Type: application/json' -d '{"exclude_paths":[".worktrees/","out.noindex/","data/qdrant/","node_modules/","dist/"]}'
# Index + poll
curl -sS -X POST http://127.0.0.1:58012/api/index -H 'Content-Type: application/json' -d '{"repo_id":"example-corpus","repo_path":"/path/to/documents","force_reindex":true}'
while true; do curl -sS http://127.0.0.1:58012/api/index/example-corpus/status | python -m json.tool; sleep 2; donecurl -sS -X POST http://127.0.0.1:58012/api/search \
-H 'Content-Type: application/json' \
-d '{"corpus_id":"example-corpus","query":"How does the retrieval pipeline work?","top_k":8,"include_vector":true,"include_sparse":true,"include_graph":true}' \
| python -m json.toolSuccess criteria:
debug.fusion_graph_attemptedistruedebug.fusion_graph_errorisnulldebug.fusion_graph_hydrated_chunks > 0- At least one match has
"source": "graph"
- Gitignore semantics: indexing uses a gitignore-aware loader that supports nested
.gitignorefiles. - Always-ignored (even if corpus has no
.gitignore):.git/,.venv/,node_modules/,__pycache__/,*.pyc,.DS_Store,.env. - Size skip (LAW): files larger than
min(chunking.max_indexable_file_size, indexing.index_max_file_size_mb*1024*1024)are skipped before reading/chunking. - Corpus-level excludes: set
exclude_pathson the corpus (stored in Postgrescorpora.meta) to skip large build outputs that are not gitignored. You can set this via UI (RAG → Indexing → Corpus settings) or API (PATCH /api/corpora/{corpus_id}).
- Neo4j health in
docker compose psmay say "starting" forever: the Neo4j container healthcheck usescurl, but the Neo4j image doesn't ship curl. Use cypher-shell instead:
docker compose --project-name ragweld exec neo4j cypher-shell -u neo4j -p password 'RETURN 1 AS ok;'-
Neo4j "critical error needs restart":
- Inspect the scoped service logs with
docker compose --project-name ragweld logs neo4j. - Use
./stop.shfor a non-destructive stop. Only use the separately confirmedreset-data.shworkflow when the named project volumes should be discarded.
- Inspect the scoped service logs with
-
Postgres startup failure:
- Inspect
docker compose --project-name ragweld logs postgres; do not change permissions or remove volumes without identifying the failing path first.
- Inspect
- UI (recommended):
RAG → Graphshows graph stats, communities, entity search, and entity neighborhood subgraphs.- Endpoints:
/api/graph/{corpus_id}/stats,/api/graph/{corpus_id}/entities,/api/graph/{corpus_id}/entity/neighbors?entity_id=,/api/graph/{corpus_id}/communities,/api/graph/{corpus_id}/community/{community_id}/members
- Endpoints:
- Neo4j Browser: http://localhost:7474 (neo4j/password)
- Useful Cypher examples:
MATCH (c:Chunk {repo_id:"example-corpus"}) RETURN count(c);
MATCH (d:Document {repo_id:"example-corpus"}) RETURN count(d);
MATCH (e:Entity {repo_id:"example-corpus"}) RETURN e.entity_type, count(*) ORDER BY count(*) DESC;
MATCH (e:Entity {repo_id:"example-corpus"})-[:IN_CHUNK]->(c:Chunk {repo_id:"example-corpus"}) RETURN e.name, c.file_path, c.chunk_id LIMIT 25;
SHOW INDEXES YIELD name, type, state WHERE type="VECTOR" RETURN name, state;- Indexing UI:
RAG → Indexingis fully functional and streams progress via SSE (/api/stream/operations/index?corpus_id=...). Long indexing runs won't hang if the UI isn't connected. - Corpus settings:
RAG → Indexing → Corpus settingslets you editexclude_paths(and other corpus metadata) used by indexing. - Retrieval UI:
RAG → Retrievalexposes graph retrieval toggles (e.g.,graph_search.enabled,graph_search.mode,graph_search.max_hops,graph_search.chunk_neighbor_window). - Graph UI:
RAG → Graphlets you browse communities, search entities, and load neighbor subgraphs (powered by/api/graph/*). - MCP UI:
Infrastructure → MCPshows transport status and connection info.
Pydantic validates public API payloads, persisted operator configuration, and untrusted external or cross-process data. Registered public wire schemas feed TypeScript generation; internal domain types and local UI state/view models stay with their owning modules.
Public frontend wire types are generated and never hand-copied. Explicit, typed, tested transformations are allowed at semantic boundaries. Compatibility fallbacks, shape guessing, dual contracts, and competing transport truths are not.
registered Pydantic schemas ──► generate_types.py ──► web/src/types/generated.ts
(API + config) (script) (wire types)
│ │
▼ ▼
FastAPI uses React components use
these types these types
ragweld/
├── server/ # Python FastAPI backend
│ ├── api/ # REST endpoints
│ │ ├── search.py # /api/search - tri-brid retrieval
│ │ ├── chat.py # /api/chat - conversational RAG + tracing
│ │ ├── index.py # /api/index - corpus indexing
│ │ ├── repos.py # /api/repos - corpus management
│ │ ├── config.py # /api/config - configuration + MCP status
│ │ ├── graph.py # /api/graph - knowledge graph queries
│ │ ├── docker.py # Docker, local runtime status, and Loki diagnostics
│ │ ├── cost.py # /api/cost - detailed cost estimation
│ │ ├── eval.py # /api/eval - evaluation framework
│ │ └── health.py # /api/health - service health
│ ├── mcp/ # MCP server implementation
│ │ ├── server.py # FastMCP server singleton
│ │ └── tools.py # MCP tool implementations
│ ├── db/
│ │ ├── postgres.py # corpus registry, chunk rows, summaries, caches
│ │ └── neo4j.py # Graph database operations
│ ├── indexing/
│ │ ├── chunker.py # Code-aware chunking
│ │ ├── embedder.py # Embedding generation
│ │ ├── graph_builder.py # Entity/relationship extraction
│ │ └── loader.py # File loading with gitignore support
│ ├── models/
│ │ └── tribrid_config_model.py # THE source of truth (~500+ fields)
│ ├── retrieval/
│ │ ├── vector.py # Dense retrieval
│ │ ├── sparse.py # BM25/FTS retrieval
│ │ ├── graph.py # Graph traversal
│ │ ├── fusion.py # RRF and weighted fusion
│ │ └── rerank.py # Reranking (local, cloud, learning)
│ └── services/
│ ├── rag.py # RAG orchestration + ChatDebugInfo
│ ├── traces.py # Local trace store (ring buffer)
│ └── config_store.py # Configuration persistence
│
├── web/ # React TypeScript frontend
│ └── src/
│ ├── components/ # UI components
│ │ ├── Dashboard/ # System status, metrics
│ │ ├── RAG/ # Search, config panels
│ │ ├── Chat/ # Conversational interface + debug footer
│ │ └── Infrastructure/ # Service management, MCP status
│ ├── stores/ # Zustand state management
│ ├── hooks/ # React hooks
│ ├── types/
│ │ └── generated.ts # Auto-generated from Pydantic
│ └── api/ # API client
│
├── data/
│ ├── models.json # LLM/embedding model definitions + pricing
│ └── glossary.json # UI tooltip definitions (~250 terms)
│
├── infra/ # Docker and deployment configs
├── scripts/ # Development and maintenance scripts
├── tests/ # Test suite
└── mkdocs/ # Documentation site
ragweld is highly configurable. Every parameter is defined in Pydantic with validation, defaults, and descriptions.
| Section | What it controls |
|---|---|
retrieval |
Top-K per leg, BM25 parameters, query expansion, confidence thresholds |
fusion |
Method (RRF/weighted), per-leg weights, normalization |
graph_storage |
Max hops, entity types, relationship types, community detection |
reranking |
Mode (none/local/cloud/trained), model selection, top-N |
embedding |
Provider, model, dimensions, batch size |
chunking |
Strategy, max tokens, overlap |
indexing |
Postgres config, concurrent workers |
tracing |
Local trace capture, sampling rate, retention |
mcp |
MCP server enable, mount path, defaults |
ui |
Debug footer visibility, streaming, history limits |
- UI: RAG tab in the web interface
- API:
GET/PUT /api/config - File:
tribrid_config.json - Per-corpus:
?repo_id=...parameter for corpus-specific settings
curl -X PUT "http://localhost:58012/api/config" \
-H "Content-Type: application/json" \
-d '{
"fusion": {
"vector_weight": 0.5,
"sparse_weight": 0.3,
"graph_weight": 0.2
}
}'| Endpoint | Method | Description |
|---|---|---|
/api/search |
POST | Tri-brid search with fusion and optional reranking |
/api/answer |
POST | RAG-powered answer generation with citations |
/api/chat |
POST | Conversational RAG with history and tracing |
/api/chat/stream |
POST | Streaming chat with SSE |
| Endpoint | Method | Description |
|---|---|---|
/api/repos |
GET | List all corpora |
/api/repos |
POST | Create a new corpus |
/api/repos/{id} |
GET | Get corpus details |
/api/repos/{id} |
PATCH | Update corpus settings |
/api/repos/{id} |
DELETE | Delete a corpus |
| Endpoint | Method | Description |
|---|---|---|
/api/index/start |
POST | Start indexing a corpus |
/api/index/{id}/status |
GET | Get indexing progress |
/api/index/{id}/stats |
GET | Get index statistics |
| Endpoint | Method | Description |
|---|---|---|
/api/graph/{id}/entities |
GET | List entities in the graph |
/api/graph/{id}/communities |
GET | List detected communities |
/api/graph/{id}/stats |
GET | Graph stats (entity/rel/community counts) |
/api/graph/{id}/entity?entity_id= |
GET | Fetch a single entity |
/api/graph/{id}/entity/relationships?entity_id= |
GET | Outgoing relationships of one entity (404 when the corpus has no promoted graph) |
/api/graph/{id}/entity/neighbors?entity_id= |
GET | Entity neighborhood subgraph |
/api/graph/{id}/query |
POST | Read-only Cypher query (debug) |
| Endpoint | Method | Description |
|---|---|---|
/api/traces/latest |
GET | Get latest trace (optional run_id or repo filter) |
/api/loki/status |
GET | Check Loki reachability |
/api/stream/loki/tail |
GET | SSE stream of live logs |
| Endpoint | Method | Description |
|---|---|---|
/api/mcp/status |
GET | MCP transport status |
/api/mcp/rag_search |
GET | Debug endpoint for quick tri-brid search |
/mcp/ |
POST | MCP Streamable HTTP endpoint (for MCP clients) |
| Endpoint | Method | Description |
|---|---|---|
/api/config |
GET | Get current configuration |
/api/config |
PUT | Update configuration |
/api/models |
GET | List available models |
| Endpoint | Method | Description |
|---|---|---|
/api/cost/estimate |
POST | Detailed cost breakdown (gen + embed + rerank) |
/api/cost/history |
GET | Historical cost records |
/api/cost/summary |
GET | Cost summary by period |
| Endpoint | Method | Description |
|---|---|---|
/api/eval/run |
POST | Run evaluation suite |
/api/eval/results |
GET | Get evaluation results |
Full OpenAPI documentation: http://localhost:58012/docs
# Install dependencies
uv sync
# Run tests
uv run pytest
# Type checking
uv run mypy server
# Linting
uv run ruff check server
# Format code
uv run ruff format server
# Regenerate TypeScript types (after changing Pydantic models)
uv run python scripts/generate_types.py
# Validate type sync
uv run python scripts/validate_types.py- Add to Pydantic first: Define the field in
tribrid_config_model.py - Regenerate types: Run
scripts/generate_types.py - Implement backend: Use the new field in your Python code
- Implement frontend: Use the generated TypeScript type
- Add tests: Both Python and Playwright
# Unit tests
uv run pytest tests/unit
# API tests
uv run pytest tests/api
# Integration tests (requires running services)
uv run pytest tests/integration
# Frontend type and production-build checks
npm --prefix web run lint
npm --prefix web run build
# E2E tests
web/node_modules/.bin/playwright test --config playwright.config.ts| Service | URL | Credentials |
|---|---|---|
| API Docs | http://localhost:58012/docs | - |
| Grafana | http://localhost:3301 | admin/admin |
| Prometheus | http://localhost:59090 | - |
| Loki | http://localhost:53100 | - |
| Neo4j Browser | http://localhost:7474 | neo4j/password |
curl http://localhost:58012/api/healthReturns dependency-free process liveness. Use readiness for live store status:
curl http://localhost:58012/api/readycurl http://localhost:58012/metricsPrometheus-format metrics for retrieval latency, throughput, error rates.
| Provider | Models | Notes |
|---|---|---|
| OpenAI | text-embedding-3-small, text-embedding-3-large |
Best general-purpose |
| Voyage | voyage-code-2, voyage-large-2 |
Optimized for code |
| Local | Any sentence-transformers model | No API costs |
Configure in .env or via the UI.
| Mode | Model | Notes |
|---|---|---|
none |
- | Fastest, use fusion scores only |
local |
ms-marco-MiniLM-L-6-v2 |
Good balance of speed/quality |
cloud |
Cohere, Voyage, Jina | Best quality, API costs |
trained |
Your fine-tuned model | Custom domain adaptation |
The /api/cost/estimate endpoint provides detailed breakdowns:
curl -X POST "http://localhost:58012/api/cost/estimate" \
-H "Content-Type: application/json" \
-d '{
"gen_provider": "openai",
"gen_model": "gpt-4o-mini",
"tokens_in": 2000,
"tokens_out": 500,
"embed_provider": "openai",
"embed_model": "text-embedding-3-small",
"embeds": 10,
"requests_per_day": 100
}'Response includes:
- Per-request cost: Generation + embedding + reranking
- Daily/monthly projections: Based on requests_per_day
- Detailed breakdown: Costs per component with model pricing info
- Validation errors: Missing model definitions or pricing data
Full documentation: https://dmontgomery40.github.io/ragweld/
Build locally:
mkdocs serve- Fork the repository
- Create a feature branch
- Make your changes (Pydantic first!)
- Run tests and type checking
- Submit a pull request
GPL-3.0-or-later. See LICENSE for details.
Three search engines. One answer.







