Skip to content

Leon-Rud/git-ask

Repository files navigation

git-ask

Ask questions about any GitHub repository. Paste a repo URL, wait for it to be ingested, then chat.

demo

How it works

The app uses RAG (Retrieval-Augmented Generation) to answer questions about code. Instead of feeding an entire repo to an LLM, it converts code into vector embeddings - numerical representations that capture semantic meaning - and stores them in a vector database (ChromaDB). When you ask a question, it's embedded the same way and matched against stored vectors using similarity search, so only the most relevant code snippets are sent to the LLM as context.

  1. Ingest - the backend clones the repo -> splits files into chunks -> embeds them with Ollama -> stores vectors in ChromaDB.
  2. Query - typing in chat and sending a question -> question gets embedded -> the closest chunks are retrieved -> an LLM generates an answer with source references.

Architecture

flowchart TD
    UI(["React UI :3000"]) -->|HTTP| API["FastAPI :8000"]

    subgraph Ingest ["/api/ingest"]
        GH["GitHub (clone)"] --> CH["Chunker (split)"]
        CH --> EMB["Embedder"]
    end

    subgraph Query ["/api/query"]
        RET["Retriever (RAG)"]
    end

    API --> Ingest
    API --> Query

    EMB --> OL["Ollama :11434"]
    EMB --> DB[(ChromaDB)]

    RET --> OL
    RET --> DB
Loading

Ingest flow: Clone repo → split files into chunks → embed with Ollama → store vectors in ChromaDB.

Query flow: Embed question → retrieve closest chunks from ChromaDB → send chunks + question to LLM → return answer with source references.

Stack

  • Backend: FastAPI + Python
  • Vector DB: ChromaDB (local, no infra needed)
  • LLM + Embeddings: Ollama (llama3.2 for chat, nomic-embed-text for embeddings)
  • Frontend: React + Vite

Quick start (Docker)

git clone https://github.com/Leon-Rud/git-ask.git
cd git-ask
cp .env.example .env    # optional: edit .env to use OpenAI (see below)
docker compose up --build

This starts everything - backend, frontend, and Ollama. First run pulls the models (~2-4 GB), so it takes a few minutes. After that it's fast.

Open http://localhost:3000.

Using OpenAI instead of Ollama (optional)

OpenAI is ~20x faster and gives better answers. Before running docker compose up, open the .env file you just copied and uncomment the OpenAI lines. You'll need an API key from platform.openai.com/api-keys:

LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...

Manual setup

Prerequisites

  • Python 3.10+
  • Node.js 18+
  • Ollama running locally

Pull the required models:

ollama pull llama3.2
ollama pull nomic-embed-text

Backend

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp .env.example .env   # edit if needed
uvicorn app.main:app --reload --port 8000

Frontend

cd client
npm install
npm run dev -- --port 3000

Open http://localhost:3000.

Environment variables

Variable Default Description
LLM_PROVIDER ollama ollama or openai
OLLAMA_BASE_URL http://localhost:11434 Ollama server URL
OLLAMA_MODEL llama3.2 Chat model (Docker uses llama3.2:1b for speed)
OLLAMA_EMBED_MODEL nomic-embed-text Embedding model
OPENAI_API_KEY Required if LLM_PROVIDER=openai
OPENAI_MODEL gpt-4o-mini OpenAI chat model
CHROMA_PERSIST_DIR ./chroma_data Where ChromaDB stores data

API

Endpoint Method Body / Params Description
/api/ingest POST { repo_url, token? } Ingest a repo
/api/query POST { repo_name, question, n_results? } Ask a question
/api/status/{repo_name} GET Check ingestion status
/health GET Health check

Design decisions

Why ChromaDB? I looked at FAISS, Pinecone, and Weaviate. FAISS doesn't have metadata filtering, which I needed for storing file paths and line numbers with each chunk. Pinecone requires a cloud account and Weaviate needs a separate service to manage. ChromaDB is just pip install and it works - persistent storage, metadata queries, and it runs in-process.

Chunking by function boundaries instead of fixed-size. Fixed-size chunks (e.g. 500 tokens) split functions in half, which makes retrieved context less useful. Splitting on def/class/function boundaries keeps logical units together. For languages without a regex pattern or files that are too big, it falls back to fixed-size chunks of 60 lines.

Notes

  • Public repos work without a token. For private repos, provide a GitHub Personal Access Token - it's only used for cloning and never stored.
  • Re-ingesting a repo replaces the old data.
  • Ingestion time depends on repo size and your machine. Large repos can take several minutes.
  • Previously analyzed repos are saved in localStorage so you can resume a session without re-ingesting.

About

Chat with any GitHub repository using RAG - paste a repo URL, ingest it, and ask questions backed by source references.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors