Skip to content
Photon079Public

About

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Relic

Relic answers "why is this code the way it is?" by walking the decision chain that produced it — a line of code → the commit that wrote it → the PR that merged it → the issue that forced it → the people who decided. It reconstructs the intent behind code from a repository's own history, and every claim links back to a real source on GitHub.

Relic builds a typed knowledge graph of a repository's history and answers by traversing the relationships between symbols, commits, pull requests, issues, and people — not by matching text.

What Relic is not

Relic is not a code reviewer, a linter, or a "chat with your codebase" Q&A bot. Those tools read the code as it is now and tell you what it does. Relic reads the history and tells you why it became that way — the constraint, the bug report, the review argument, the person who made the call. It's a provenance engine, not a code assistant. (It plugs into your code assistant — see Use it from your editor.)

Built for the Cognee × WeMakeDevs hackathon — submitted to both the open-source and Cognee Cloud tracks.


Why this isn't basic RAG

Normal (vector) RAG chops text into chunks and returns whatever sounds most similar. It matches words, not relationships — so it can't answer a question whose answer is spread across a commit, a PR, an issue, and a person.

Relic stores entities (File, Commit, PullRequest, Issue, Person) and the typed edges between them, then walks those edges. The answer is composed only from the traversed subgraph, so it can't hallucinate beyond what the graph actually says — and when the chain doesn't exist, it says so instead of guessing.

Relic ships a live side-by-side: ask the same question of flat vector RAG and of Relic's graph walk, over the same embedded data, and watch flat RAG miss the chain.


Features

  • Repo ingestion — pulls commits, PRs, review comments, issues, and per-commit file diffs (with line ranges) via the GitHub GraphQL + REST APIs, paginated past the 100-per-page cap.
  • Typed knowledge graph — maps history into Cognee DataPoint nodes with typed edges (modifies, part_of, closes, authored_by, opened_by, reviewed_by, raised_by, supersedes…), scoped to a per-repo NodeSet.
  • Code layer (tree-sitter) — parses JS/TS/Python into Symbol nodes (functions, methods, classes) and joins each commit to the symbols it actually touched, using blame-accurate historical mapping (files are re-parsed at each commit, not just at HEAD).
  • The "why" query — vector search finds the entry node, a graph walk assembles the decision chain, and the LLM composes an answer grounded only in the traversed subgraph (it can't hallucinate past the graph; when the chain doesn't exist, it says so).
  • Symbol-level retrieval — ask "why is this function like this?" and Relic anchors on the specific symbol and walks its history — the same path the editor plugin uses.
  • Reference-aware seeds — queries naming #123, issue 52, a commit SHA, or @handle resolve to that exact node instead of relying on fuzzy vector match.
  • Cited provenance — every answer lists its sources (PR #, issue #, commit SHA, @handle), each a real GitHub link.
  • Provenance graph visualization — an interactive React Flow graph of the traversed subgraph; click a node to open it on GitHub.
  • Flat-RAG comparison — a toggle to run vanilla vector RAG on the same embedded data, side by side, so you can watch flat RAG miss the chain.
  • MCP server — exposes get_decision_context(file, symbol_or_line, repo) so Claude Code / Cursor can pull a symbol's decision chain mid-edit (see below).
  • Cognee Cloud support — set cloud credentials and the graph syncs to your managed tenant; without them Relic runs fully self-hosted.
  • Honest counts — the UI shows ingested / repo-total (e.g. 150 / 812 PRs) so coverage is transparent.
  • Depth control — Standard (fast) or Deep (up to 100+ each, paginated).

Architecture

        ┌────────────────────────────────────────────┐
        │              Frontend (React + Vite)        │
        │  Ingest · Query · Provenance graph · Compare │
        └───────────────┬──────────────────────────────┘
                        │ REST (proxied in dev)
        ┌───────────────▼──────────────────────────────┐
        │              Backend (FastAPI)               │
        │  /ingest  /query  /query/naive  /graph  ...  │
        └───┬───────────────┬───────────────┬──────────┘
            │               │               │
   ┌────────▼──────┐ ┌──────▼───────┐ ┌─────▼─────────┐
   │ GitHub        │ │ Cognee       │ │ LLM (Groq)    │
   │ GraphQL API   │ │ graph+vector │ │ + fastembed   │
   │ (ingestion)   │ │ engine       │ │ (local embed) │
   └───────────────┘ └──────────────┘ └───────────────┘

The "why" query flow: question → Cognee vector search finds relevant entry nodes → graph walk over the typed edges builds the provenance subgraph → Groq composes an answer grounded only in that subgraph → response returns { answer, chain, citations, subgraph }.


Tech stack

Layer Choice
Backend Python 3.12, FastAPI, Uvicorn
Graph + memory Cognee 1.2.2 (self-hosted)
LLM Groq (llama-3.3-70b-versatile) via LiteLLM
Embeddings fastembed — local, BAAI/bge-small-en-v1.5 (384-dim), no API
Code parsing tree-sitter + tree-sitter-language-pack (JS / TS / Python)
Ingestion GitHub GraphQL + REST API + personal access token
Editor integration MCP server (FastMCP)
Frontend React 18, Vite, React Flow

Why Groq + fastembed? Groq offers a generous free tier for the LLM, and fastembed runs embeddings locally with no API key and no rate limits — so ingesting a real repo (hundreds of nodes) never hits a quota wall.


Prerequisites

  • Python 3.12 and pip
  • Node.js 18+ and npm
  • A Groq API key — free at console.groq.com (starts with gsk_)
  • A GitHub Personal Access Token — github.com/settings/tokens, classic with public_repo scope is enough (starts with ghp_)

Setup

git clone https://github.com/Photon079/Relic.git
cd Relic

1. Backend

python3.12 -m venv venv
source venv/bin/activate            # Windows: venv\Scripts\activate
pip install -r backend/requirements.txt

The first run downloads the fastembed model (~50 MB, one time).

2. Configure secrets

cp .env.example .env

Then fill in the two empty values in .env:

LLM_API_KEY="gsk_your_groq_key"
GITHUB_TOKEN="ghp_your_github_token"

Everything else (Groq model, local fastembed embeddings) is preconfigured — no other keys needed.

3. Frontend

cd frontend
npm install

Running

Open two terminals from the repo root.

Terminal 1 — backend (http://127.0.0.1:8000):

source venv/bin/activate
uvicorn backend.main:app --reload --port 8000

Terminal 2 — frontend (http://localhost:5173):

cd frontend
npm run dev

Then open http://localhost:5173, paste a small public repo (e.g. sindresorhus/slugify), click Build graph, and ask a "why" question. The interactive API docs are at http://127.0.0.1:8000/docs.

Whenever you edit .env, restart the backend — env vars are read once at startup (even --reload only watches .py files).


Usage without the UI (CLI)

The pipeline runs standalone too — handy for testing or pre-loading a graph.

# Build a repo's graph (into Cognee's local store)
python -m backend.graph.builder sindresorhus/slugify --depth standard --prune

#   --depth standard | deep   (deep = up to 150 commits/PRs/issues, paginated)
#   --prune                   wipe all prior Cognee data first (clean slate)

# Ask a "why" question against the built graph
python -m backend.retrieval.query "Why does slugify support an empty separator?"

# Day-0 sanity check: prove Cognee ingests the typed ontology cleanly
python scripts/validate_cognee.py

Use it from your editor (MCP)

Relic ships an MCP server so a coding assistant (Claude Code, Cursor) can ask for a symbol's provenance while you edit it — before you change a function, pull the history that shaped it.

# Run the server (stdio transport)
python -m backend.mcp.server

Register it with your assistant (Claude Code example):

claude mcp add relic -- python -m backend.mcp.server

It exposes one tool:

Tool Args Returns
get_decision_context file, symbol_or_line, repo? { symbol, why, decision_chain[], citations[] }

symbol_or_line accepts a name ("pLimit") or a line number ("42", resolved to the enclosing symbol). The assistant gets back the same grounded "why" + ranked commit→PR→issue chain + GitHub citations the UI shows — so its edit suggestions are informed by why the code is that way, not just what it says.


API reference

Base URL: http://127.0.0.1:8000

Method Route Body / params Returns
GET /health — { status: "ok" }
POST /ingest { repo_url, depth? } { repo_id, status: "processing" }
GET /ingest/{repo_id}/status — { status, node_types, repo_totals, ... }
POST /query { question, repo_url? } { answer, chain, citations, subgraph }
POST /query/naive { question, repo_url? } { answer, sources } (flat-RAG baseline)
GET /graph/{repo_id} ?files=true (optional) { repo, subgraph } (full ingested graph)

Ingestion is asynchronous: POST /ingest returns immediately, and you poll /ingest/{repo_id}/status until status is "done" (or "error"). repo_id is owner/name.

Example

# Start an ingest
curl -X POST http://127.0.0.1:8000/ingest \
  -H "Content-Type: application/json" \
  -d '{"repo_url":"sindresorhus/slugify","depth":"standard"}'

# Poll status
curl http://127.0.0.1:8000/ingest/sindresorhus/slugify/status

# Ask why
curl -X POST http://127.0.0.1:8000/query \
  -H "Content-Type: application/json" \
  -d '{"question":"Why does slugify support an empty separator?","repo_url":"sindresorhus/slugify"}'

Project structure

Relic/
├── backend/
│   ├── main.py                 # FastAPI app, CORS, route mounting
│   ├── config.py               # env + settings (loaded before Cognee)
│   ├── ingestion/
│   │   ├── github_client.py    # GraphQL fetch (paginated) + repo totals
│   │   └── mappers.py          # raw JSON -> wired DataPoint instances
│   ├── ingestion/
│   │   └── code_parser.py      # tree-sitter -> Symbol nodes (JS/TS/Python)
│   ├── graph/
│   │   ├── models.py           # Cognee DataPoint ontology (nodes + edges)
│   │   ├── symbols.py          # join commits -> symbols (blame-accurate)
│   │   ├── cloud.py            # optional Cognee Cloud sync
│   │   └── builder.py          # add_data_points pipeline, NodeSet scoping
│   ├── retrieval/
│   │   ├── query.py            # vector entry -> graph walk -> grounded answer
│   │   ├── citations.py        # nodes -> GitHub-linked citations
│   │   └── naive.py            # flat vector-RAG baseline (the foil)
│   ├── mcp/
│   │   └── server.py           # MCP server: get_decision_context tool
│   ├── api/
│   │   ├── routes_ingest.py    # async ingest + status
│   │   ├── routes_query.py     # /query and /query/naive
│   │   ├── routes_graph.py     # /graph/{repo_id}
│   │   └── schemas.py          # request/response models
│   └── requirements.txt
├── frontend/
│   ├── src/
│   │   ├── App.jsx             # main flow + state
│   │   ├── api/client.js       # fetch wrappers
│   │   ├── entities.js         # shared entity colors
│   │   └── components/         # RepoInput, AnswerPanel, CitationList,
│   │                           #   ProvenanceGraph, ComparePanel
│   ├── vite.config.js          # dev server + API proxy
│   └── package.json
├── scripts/
│   └── validate_cognee.py      # Day-0 Cognee validation harness
├── .env.example
└── README.md

The data model

Nodes are Pydantic classes inheriting from Cognee's DataPoint. Assigning one node to another's field creates a typed edge (the field name is the relationship).

Node Key properties
File path
Commit sha, message
PullRequest number, title, discussion
Issue number, title, body, state
Person handle
Symbol name, kind, path, start/end line
Edge Meaning
Commit → modifies → File / Symbol a commit changed this file / symbol
Commit → part_of → PullRequest this commit belongs to a PR
PullRequest → closes → Issue this PR resolved an issue
Issue → raised_by → Person who reported it
Commit → authored_by → Person who wrote it
PullRequest → opened_by → Person who opened it
PullRequest → reviewed_by → Person who reviewed it
Issue → assigned_to → Person who's assigned
Commit → supersedes → Commit a later change to the same code

Limitations & notes

  • Coverage is capped per ingest. Standard pulls ~40 PRs; Deep up to ~150. Large repos have far more — the stat bar shows ingested / total so the gap is visible. Raise it with the depth toggle or RELIC_PR_LIMIT / RELIC_COMMIT_LIMIT / RELIC_ISSUE_LIMIT in .env.
  • Best on repos with real PR/issue history. The value is the decision chain; a repo of direct-to-main commits with no PRs yields a thin answer (by design, not a bug).
  • Single-process store. Cognee's embedded DB is single-process — don't run a query during an ingest (the UI prevents this).
  • .env is git-ignored. Your keys never get committed; each contributor supplies their own.

Deployment

Relic deploys as a Dockerized FastAPI backend (Render blueprint in render.yaml) plus a static frontend (Vercel/Netlify), with optional Cognee Cloud sync for the managed tenant. Full walkthrough in DEPLOY.md.


Roadmap

  • "Why" enrichment via Cognee cognify (extract decisions, constraints, tradeoffs as first-class nodes)
  • Blast-radius view ("what breaks if I change this") and expert-finder ("who knows this symbol")
  • More languages in the code layer (Go, Rust, Java)
  • Full-history pagination and incremental refresh on new commits

About

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages