An on-premises RAG system that helps engineers diagnose bugs faster by searching historical fixes using natural language. Describe a crash, hang, or error in plain English and the system retrieves the most relevant historical bug fixes, root causes, and actual code patches — then synthesizes a grounded, cited answer using an LLM. No data leaves your infrastructure unless you choose a cloud LLM provider.
- What it does: Type a bug description → get a cited answer with root cause, explanation, and code fix
- How it works: Hybrid BM25 + vector search over your git history, answered by an LLM
- What it needs: Docker, Python 3.11+, Node.js 18+, a free Google Gemini API key
- How to run:
cp .env.example .env→ add API key →./start_all.sh→ open http://localhost:3000 - Bring your own repo: Point the ingest scripts at any git repository, not just the included pandas demo
Follow these three steps to get a local instance running for development or evaluation.
- Copy the environment template and set secrets:
cp .env.example .env
# Edit .env and set your API key(s). Example:
# GOOGLE_API_KEY=your_key_here
# GOOGLE_MODEL=gemini-3.1-flash-lite- Start services:
./start_all.sh- Open the UI in your browser:
Key environment variables used by the project. Put these in .env or export them in your shell for local runs.
POSTGRES_URL— Postgres connection string (e.g.postgresql://user:pass@localhost:5432/dbname)GOOGLE_API_KEY— Google Gemini API key (optional if using another LLM provider)GOOGLE_MODEL— e.g.gemini-3.1-flash-lite(provider-specific model id)FLAG_EMBEDDING_MODEL— embedding model id (default:BAAI/bge-small-en-v1.5)PGVECTOR_DIM— embedding dimension (must match the model, default384)PROJECT— project name used by ingestion scripts (set before running ingestion)
Example .env snippet:
# PostgreSQL
POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/debugdb
# LLM
GOOGLE_API_KEY=your_key_here
GOOGLE_MODEL=gemini-3.1-flash-lite
# Embeddings
FLAG_EMBEDDING_MODEL=BAAI/bge-small-en-v1.5
PGVECTOR_DIM=384
# Ingest
PROJECT=pandas
demo1.mp4
Open http://localhost:3000 and describe your bug in plain English.
Example queries:
crash in generic.py around line 2393 not related to scheduler
DataFrame groupby aggregate memory error
BUG duplicated loses index
hang in the dispatcher not a.cpp
What you get back:
| Panel | Content |
|---|---|
| TL;DR | One-sentence summary of the root cause and fix |
| Explanation | 2–3 paragraph analysis grounded in the historical commit |
| Code Fix | The actual diff from the fix, syntax-highlighted |
| Citations | Commit SHAs linking back to the source |
Query tips:
- Include a signal word: crash, hang, segfault, memory error, assertion, timeout
- Name a file or function if you know it:
groupby,generic.py,DataFrame - Use negations to exclude irrelevant matches:
not scheduler,not a.cpp - Vague queries like
"it crashed"trigger a clarifying follow-up — add more detail and resubmit
| Requirement | Version |
|---|---|
| Python | 3.11+ |
| Node.js | 18+ |
| Docker | 24+ with Docker Compose |
| RAM | 8 GB minimum (16 GB recommended for embedding generation) |
| API key | Google Gemini — free tier sufficient for single-engineer use |
git clone https://github.com/pakapoor/ai_enabled_debug_assistant.git
cd ai_enabled_debug_assistantcp .env.example .envOpen .env and set your Gemini API key:
GOOGLE_API_KEY=your_key_here
GOOGLE_MODEL=gemini-3.1-flash-liteGet a free key at aistudio.google.com. All other values in .env.example work as-is for a local deployment.
Dependencies are installed automatically when you run ./start_all.sh.
If you prefer to install manually first, run:
bash setup_dependencies.shTo use your own repo:
-
Clone the target repository:
git clone https://github.com/your-org/your-repo.git ~/rag_data/your-repo -
Open
ingest_commits.pyandcreate_patch_child_chunks.pyand set thePROJECTvariable at the top of each file:PROJECT = "your-repo" # must match in both files
-
Optionally adjust
LIMIT = 500iningest_commits.pyto control how many commits to ingest.
To try the included pandas demo, clone pandas first:
git clone https://github.com/pandas-dev/pandas.git ~/rag_data/pandas
# then set PROJECT = "pandas" in both ingest filesThen run the three ingestion stages:
# Stage 1: ingest commits with full patches
python3 ingest_commits.py
# Stage 2: generate AST-enriched per-file child chunks
python3 create_patch_child_chunks.py
# Stage 3: compute BGE-small-en-v1.5 embeddings
python3 app/compute_embeddings.pyExpected result: ~2,037 chunks with embeddings in Postgres.
./start_all.shThis starts all services automatically:
| Service | URL |
|---|---|
| Hybrid search API | http://localhost:8001 |
| Answer API | http://localhost:8002 |
| React UI | http://localhost:3000 |
Open http://localhost:3000. The system is ready. See the Usage section above for example queries and tips.
Engineer types query
│
▼
query_understanding.py ──► LLM extracts structured JSON
{ signal, file, function, negations, confidence }
│
├── confidence = "low" ──► Return follow-up question (no LLM inference wasted)
│
▼
hybrid_api.py (port 8001)
├── BM25 full-text search (PostgreSQL tsvector)
├── Vector cosine search (pgvector, BGE-small-en-v1.5, 384-dim)
└── Merge via custom RRF scoring
│ + bug boost (+50)
│ - doc penalty (-30)
▼
answer_api.py (port 8002)
├── prompt_builder.py (grounded context injection)
├── llm_client.py (provider-agnostic LLM call)
└── parse structured response → { tldr, explanation, code, citations }
│
▼
React UI (port 3000)
TL;DR panel │ Explanation panel │ Code Fix (diff, syntax-highlighted) │ Citations
- One-click onboarding — paste a Git URL and the system clones, ingests, and indexes your repo automatically, no manual scripts needed
- Recency-weighted blame search — ask "what changed recently that broke this?" and the system surfaces commits from the past 30/60/90 days weighted by recency, not just historical fixes
- Search your whole codebase — opt in to index all source files, not just changed files, so you can ask questions about any function or class even if it was never in a bug fix commit
- Answers as they stream — results appear word by word as the LLM generates them, no waiting for the full response
- Smarter result ranking — a second-pass reranker filters candidates for relevance before presenting results, reducing noise in the top 3
- Repeat queries answered instantly — results are cached for 24 hours so the same query returns immediately on second ask
- Multi-agent reasoning — separate specialized agents for query understanding, retrieval, and fix suggestion work in parallel for richer answers
- Team-scale deployment — authentication layer and multi-user support for shared team access
- Fast vector search at scale — HNSW approximate nearest-neighbor index enabled for sub-millisecond search as your corpus grows beyond thousands of chunks
- Automated test suite — integration tests for the full pipeline against a fixed fixture corpus so contributors can validate changes confidently
| Layer | Technology |
|---|---|
| Vector store | PostgreSQL 16 + pgvector |
| Embeddings | BGE-small-en-v1.5 (384-dim, FlagEmbedding) |
| Full-text search | PostgreSQL tsvector / ts_rank_cd |
| Backend APIs | FastAPI + Uvicorn |
| LLM interface | Google Gemini (default), Groq, Ollama |
| Frontend | React 19, Axios, react-syntax-highlighter |
| Infrastructure | Docker Compose |
| Language | Python 3.11, JavaScript (React) |
| Data source | pandas GitHub repository (500 commits, ~2,037 chunks) |
Pinecone and Qdrant are purpose-built vector stores but they split your data across two systems — a vector index and a relational database for metadata. Keeping everything in PostgreSQL gives transactional atomicity (embeddings and metadata update together or not at all), full SQL expressiveness for filtering, and keeps all engineer data on-premises. pgvector's HNSW index handles the vector search with comparable latency at this scale.
Pure vector search struggles with exact technical terms (function names, error codes, file paths). Pure keyword search misses semantic similarity. The system runs both in parallel and merges results with a custom Reciprocal Rank Fusion variant:
base_score = 100 - (rank × 5)
final_score = base_score + bug_boost - doc_penalty
- Bug boost (+50): Commits prefixed
BUG:are surfaced above general refactors - Documentation penalty (-30):
.rst,.md, andwhatsnew/files are deprioritized — engineers want code, not changelog entries
Git commits are ingested at two levels:
- Parent chunk — full commit: subject, body, author, date, files changed, and truncated patch (20,000 char cap). Ingested by
ingest_commits.py. - Child chunks — one chunk per changed file within the patch, extracted by
create_patch_child_chunks.py. Each child chunk adds:- Python AST parsing to identify the enclosing class and function for every hunk start line
- Full function source (up to 120 lines) injected as
FUNCTION CONTEXT - Hunk headers extracted for structural context
This means a query about DataFrame.groupby() can hit the exact function body rather than a 500-line commit blob.
Before touching the search index, query_understanding.py sends the raw query to the LLM and asks for structured JSON:
{
"signal": "crash",
"file": "generic.py",
"function": "groupby",
"line": 2393,
"stack_trace": false,
"keywords": ["groupby", "aggregate", "crash"],
"negations": ["scheduler"],
"confidence": "high",
"follow_up": null
}If confidence is "low" (fewer than two qualifying signals present), the API returns a follow-up question immediately — no search, no LLM generation call, no latency. Negations like "not scheduler" are extracted and surfaced in the UI so the engineer knows what was excluded.
The answer API instructs the LLM to respond in a fixed three-section format:
TLDR:
<one sentence>
EXPLANATION:
<2-3 paragraphs>
CODE:
<raw diff lines, no fences>
answer_api.py parses this deterministically into separate fields. The React frontend renders each section in its own panel, with the CODE section piped through react-syntax-highlighter using the diff language and VSCode Dark+ theme.
llm_client.py is a single-function interface:
def ask_llm(prompt: str, timeout: int = 60) -> str: ...Swapping providers is a one-file change. Tested with:
- Ollama phi3:3.8b — local CPU inference (slow, inconsistent instruction following)
- Groq llama-3.3-70b — fast cloud inference, good quality
- Google Gemini 1.5 Flash / 2.0 Flash — current default, best quality/cost ratio
.
├── answer_api.py # FastAPI port 8002 — full pipeline endpoint
├── hybrid_api.py # FastAPI port 8001 — hybrid search endpoint
├── query_understanding.py # LLM-based structured query extraction
├── llm_client.py # Provider-agnostic LLM interface
├── prompt_builder.py # Grounded prompt construction
├── config.py # Env-var configuration
│
├── ingest_commits.py # Stage 1: commit ingestion with patches
├── ingest_commits_legacy.py # Stage 1 (legacy, no patch content)
├── create_patch_child_chunks.py # Stage 2: AST-enriched per-file chunks
├── app/compute_embeddings.py # Stage 3: BGE embeddings → DB
│
├── debug-assistant-ui/ # React frontend
│ └── src/App.js # TL;DR / Explanation / Code Fix / Citations
│
├── app/
│ ├── main.py # Legacy FastAPI (keyword search only)
│ ├── load_db.py # Legacy YAML bug loader
│ ├── ingest.py # Legacy ingestion
│ └── search.py # Legacy search
│
├── tests/
│ ├── test_query_understanding.py
│ ├── test_function_context.py
│ ├── test_function_locator.py
│ └── test_ask_model.py
│
├── bugs/ # Synthetic YAML bug reports (dev/testing)
├── config/projects.yml # Project registry
├── init.sql # PostgreSQL schema + indexes
├── docker-compose.yml # Postgres + pgvector
├── .env.example # Environment variable template
│
├── start_all.sh # Starts all services (Postgres, APIs, React UI)
├── setup_dependencies.sh # Auto-installs Python packages and React dependencies
├── stop_all.sh # Stops all running services cleanly
│
├── hybrid_search_cli.py # CLI: run hybrid search interactively
├── delete_commits.py # Dev utility: wipe chunks table
├── prompt_from_search.py # Test harness: print built prompt
├── search_ui.py # Tkinter debug UI (legacy)
│
└── generate_bugs.py / generate_custom_bugs.py / generate_detailed_bugs.py
# Synthetic bug YAML generators
CREATE TABLE chunks (
id TEXT PRIMARY KEY, -- e.g. "my-repo-commit-abc123-patch-002"
project TEXT NOT NULL, -- your PROJECT name, e.g. "my-repo"
source_type TEXT NOT NULL, -- "git_commit" | "git_patch_file"
source_id TEXT NOT NULL, -- commit SHA
parent_id TEXT, -- set on child chunks
chunk_index INT NOT NULL,
chunk_text TEXT NOT NULL,
metadata JSONB NOT NULL, -- commit, author, date, files, is_bug, function_names...
embedding vector(384), -- BGE-small-en-v1.5
created_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ
);
-- GIN index for full-text search
CREATE INDEX ON chunks USING GIN(to_tsvector('english', chunk_text));
-- GIN index for JSONB metadata filtering
CREATE INDEX ON chunks USING GIN(metadata);
-- HNSW vector index (enable after bulk load — commented out in init.sql)
-- CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);Two-tier, source-aware chunking:
- Commit messages + file lists → parent chunks (
git_commitsource type) - Per-file patches → child chunks (
git_patch_filesource type)
For code chunks, Python's built-in ast module parses each changed file to identify the enclosing class and function for every hunk start line. The full function source (up to 120 lines) is injected as FUNCTION CONTEXT into each child chunk. This means retrieval hits the exact function body, not an arbitrary 500-token window.
For oversized functions (>120 lines), the function source is truncated with a note — a windowed-diff fallback is on the V2 roadmap.
Why not fixed-size chunking? Fixed-size chunking splits functions mid-logic. A query about DataFrame.groupby() would get a random 500-token window rather than the complete function, destroying retrieval quality.
pgvector was chosen because one row holds vector + text + metadata atomically. A single query can do semantic search, keyword search, and metadata filtering together without joining across two systems.
| Option | Verdict | Reason |
|---|---|---|
| pgvector | Chosen | Atomic row, hybrid search native, self-hostable |
| Pinecone | Rejected | Data leaves perimeter, managed only |
| Qdrant | Close alternative | Loses one-row atomicity |
| Weaviate | Rejected | JVM overhead for capability Postgres already provides |
| Milvus | Rejected | Billion-vector scale — overkill at 2K chunks |
Past ~5M chunks, pgvector has no native sharding — path would be Citus extension or app-level partitioning by project.
Pure vector search blurs exact technical terms (function names, file paths, error codes). Pure BM25 misses semantic paraphrases ("SIGABRT" ≈ "SIGSEGV" ≈ "program crashed"). The system runs both in parallel:
BM25 top-10 + vector top-10 → custom RRF scoring → top-3
Custom scoring formula:
base_score = 100 - (rank × 5)
final_score = base_score + bug_boost - doc_penalty
bug_boost = +50 (chunks where is_bug = True)
doc_penalty = -30 (chunks from .rst, .md, whatsnew/ files)
The bug boost ensures actual fix commits surface above general refactors. The doc penalty ensures engineers get code patches, not changelog entries.
Metadata lives in Postgres JSONB alongside the vector and text, not in a separate MongoDB instance. MongoDB's "schemaless" flexibility is a property of the JSON data model, not MongoDB-exclusive — JSONB provides identical flexibility. Keeping everything in one row preserves atomicity: vector, text, and metadata update together or not at all.
| Option | Verdict | Reason |
|---|---|---|
| Postgres JSONB | Chosen | Same schema flexibility, zero extra system, atomicity preserved |
| MongoDB (separate) | Rejected | Breaks atomicity — vector and metadata would need to be joined across two systems |
bge-small-en-v1.5 (384-dim, BAAI) was chosen for V1 based on hardware constraints — developed and tested on a Snapdragon X laptop (16GB RAM, WSL2 Ubuntu) and Mac M2 mini (8GB RAM, macOS). Larger models (bge-large-en-v1.5, 1024-dim) would produce higher-quality embeddings but were too slow for iterative development on these machines.
The model runs fully self-hosted via FlagEmbedding — no API calls, no data leaving the machine.
| Option | Verdict | Reason |
|---|---|---|
| bge-small-en-v1.5 (384-dim) | Chosen | Fast on CPU, self-hostable, fits V1 hardware |
| bge-large-en-v1.5 (1024-dim) | V2 candidate | Higher quality but too slow for CPU-only iteration |
| bge-m3 (multilingual, 1024-dim) | V2 candidate | Multilingual not needed for English-only corpus |
| OpenAI / Cohere (API-based) | Rejected | Data leaves perimeter for any on-prem deployment |
Cosine similarity measures the angle between vectors, ignoring magnitude. This matters for text because chunk length inflates vector magnitude arbitrarily — a short query and a long commit message about the same bug shouldn't appear "far apart" just because one is longer.
In practice, V1 uses dot product on pre-normalized vectors — normalization happens once at ingestion, so every query gets cosine correctness at dot product speed.
The HNSW (Hierarchical Navigable Small World) index is commented out in init.sql and should be enabled after bulk load in production. Without it, pgvector falls back to exact sequential scan — acceptable at 2K chunks, does not scale.
Three index types in use:
| Index | Column | Purpose |
|---|---|---|
| HNSW | embedding |
Approximate nearest-neighbor vector search |
| GIN | to_tsvector(chunk_text) |
BM25-equivalent full-text search |
| GIN | metadata |
Fast JSONB filtering (is_bug, file, source_type) |
HNSW is approximate — may occasionally miss the single true closest match. Acceptable because the system retrieves top-20 candidates before scoring, not just top-1.
Before touching the search index, every query passes through query_understanding.py, which makes a fast LLM call to extract structured JSON:
{
"signal": "crash",
"file": "generic.py",
"function": "groupby",
"line": 2393,
"stack_trace": false,
"keywords": ["groupby", "aggregate"],
"negations": ["scheduler"],
"confidence": "high",
"follow_up": null
}If confidence is "low" (fewer than two qualifying signals — signal word, file name, function name, or technical keywords), the API returns a follow-up question immediately — no search, no LLM generation, no latency wasted. A bare query like "it crashed" or "hang" alone triggers this gate.
Negations ("not scheduler", "not a.cpp") are extracted and surfaced in the UI so the engineer knows what was excluded. Spelling correction runs in the same call — "datafrme" → "DataFrame", "gruopby" → "groupby".
llm_client.py is a single-function interface — swapping providers is a one-file change:
def ask_llm(prompt: str, timeout: int = 60) -> str: ...Providers tested:
| Provider | Model | Latency | Quality | Notes |
|---|---|---|---|---|
| Ollama (local) | phi3:3.8b | ~90s | Inconsistent | CPU-only on Snapdragon X, ignores format instructions |
| Groq | llama-3.3-70b-versatile | ~2s | Excellent | Free tier: 100K tokens/day |
| Google Gemini | gemini-3.1-flash-lite | ~3s | Good | Current default |
For a production deployment with on-prem data sovereignty requirements, Llama 3 70B via vLLM on a GPU server is the right choice — Apache license, Western origin (no procurement friction in regulated industries), mature self-hosting tooling.
The answer API instructs the LLM to respond in a fixed three-section format:
TLDR:
<one sentence>
EXPLANATION:
<2-3 paragraphs>
CODE:
<raw diff lines, no markdown fences>
answer_api.py parses this deterministically by scanning for the exact section headers. Fallbacks handle non-compliant model output:
- If
TLDR:is missing → first sentence ofEXPLANATIONis used - If
CODE:contains markdown fences → stripped before rendering - If
CODE:bleeds into citations (lines starting with[commit:) → truncated at that boundary - If
negationsare present → matching results are filtered out before LLM generation (applied inanswer_api.pybefore prompt assembly)
The system prompt includes: "If the answer is not supported by the context, say I don't know." For a debugging tool, a confidently wrong answer is worse than no answer — an engineer might spend hours chasing a fabricated root cause.
V1's pipeline is fully linear and determined before the LLM is called:
query understanding → sufficiency check → hybrid search → prompt assembly → generate
Agent orchestration (LangGraph, LangChain agents) solves the problem of deciding what to do next — branching mid-flow, calling tools, multi-step reasoning. V1 doesn't have that problem. Adding an agent framework would be unjustified complexity with no payoff.
The closest V1 has to agentic behavior is the sufficiency gate in query_understanding.py — a fast LLM call that classifies query intent and decides whether to proceed to search or ask the user for more detail. This is a deliberate, lightweight form of conditional branching without the overhead of a full agent framework.
V2 roadmap includes a multi-agent architecture: a Triage Agent (query classification), Root Cause Agent (retrieval + synthesis), and Fix Suggester Agent (patch recommendation), coordinated by a LangGraph orchestrator. That's when the framework investment becomes justified.
Structured JSON logging is built into answer_api.py using Python's standard logging module. Every request writes a newline-delimited JSON entry to ~/rag_logs/answer_api.log:
{
"timestamp": "2026-06-22T10:45:23.112000",
"endpoint": "/ask",
"query": "crash in groupby.pyx when NA values",
"response_time_seconds": 3.247,
"status": "ok",
"model": "gemini-3.1-flash-lite",
"results_used": 3,
"citations": 2,
"negations": []
}Low-confidence queries that triggered a follow-up are logged with "status": "low_confidence" — making it easy to measure the sufficiency gate's false-positive rate and tune the confidence threshold over time.
Logs are written to ~/rag_logs/ (outside the project folder) to avoid Google Drive sync corruption — the same pattern used for Python virtual environments on this development setup.
Full LLM pipeline observability (per-step tracing, hallucination detection, retrieval drift monitoring) is planned for V2 via Langfuse — a self-hosted, MIT-licensed LLM observability platform that plugs directly into the existing FastAPI backend.
Local inference is slow on this hardware. The development machine is a Snapdragon X laptop running WSL2. Ollama runs CPU-only because the Adreno GPU is not yet supported — phi3:3.8B takes 2–4 minutes per query. Cloud providers (Groq, Gemini) respond in under 5 seconds and produce significantly better output.
Small local models are inconsistent. phi3:3.8B occasionally ignores the TLDR: / EXPLANATION: / CODE: format constraints, requiring fallback parsing logic in answer_api.py. Models at 30B+ (Groq llama-3.3-70b, Gemini) follow structured output instructions reliably.
Free-tier rate limits apply. Groq and Google Gemini free tiers have RPM/TPM caps. Under heavy testing this surfaces as 429 errors. Acceptable for single-engineer use; a paid tier or self-hosted 30B+ model would be needed for team deployment.
500-commit corpus is a proof of concept. The pandas dataset was chosen for its dense, well-labelled bug history (BUG: prefixes, clean patches). Real deployment requires ingesting your own repository and potentially tuning the bug-detection heuristics for your team's commit conventions.
No authentication. The APIs bind to 0.0.0.0 on localhost. Not suitable for networked or multi-user deployment without adding an auth layer.
# Reset the chunks table during development
python3 delete_commits.py
# Run hybrid search from the command line (no UI needed)
python3 hybrid_search_cli.py
# Print the full grounded prompt for a query (useful for debugging retrieval)
python3 prompt_from_search.py
# Check row and embedding counts
bash count_rows.sh
# Generate synthetic YAML bugs for testing ingestion
python3 generate_detailed_bugs.pyAll services write structured JSON logs to ~/rag_logs/ (outside the project folder to avoid Google Drive sync issues).
| Log file | Service | What it captures |
|---|---|---|
~/rag_logs/answer_api.log |
Answer API (port 8002) | Every query, response time, model used, citation count, negations applied |
~/rag_logs/hybrid_api.log |
Hybrid search API (port 8001) | Search requests, result counts, BM25/vector scores |
~/rag_logs/react_ui.log |
React UI (port 3000) | npm start output, compilation errors |
Each entry is a single JSON line (newline-delimited JSON):
{
"timestamp": "2026-06-22T10:45:23.112000",
"endpoint": "/ask",
"query": "crash in groupby.pyx when group key contains NA values",
"response_time_seconds": 3.247,
"status": "ok",
"model": "gemini-3.1-flash-lite",
"results_used": 3,
"citations": 2,
"negations": []
}Low-confidence queries that triggered a follow-up question are logged separately:
{
"timestamp": "2026-06-22T10:46:01.334000",
"endpoint": "/ask",
"query": "it crashed",
"response_time_seconds": 1.12,
"status": "low_confidence"
}# Watch live query activity
tail -f ~/rag_logs/answer_api.log
# Pretty-print the last 10 queries
tail -10 ~/rag_logs/answer_api.log | python3 -m json.tool
# Find all slow queries (over 5 seconds)
grep -h "" ~/rag_logs/answer_api.log | python3 -c "
import sys, json
for line in sys.stdin:
try:
entry = json.loads(line)
if entry.get('response_time_seconds', 0) > 5:
print(f\"{entry['timestamp']} | {entry['response_time_seconds']}s | {entry['query']}\")
except: pass
"
# Count queries by status (ok vs low_confidence)
grep -h "" ~/rag_logs/answer_api.log | python3 -c "
import sys, json
from collections import Counter
counts = Counter()
for line in sys.stdin:
try:
counts[json.loads(line).get('status', 'unknown')] += 1
except: pass
for status, count in counts.items():
print(f'{status}: {count}')
"
# Find all queries that used negations
grep -h "" ~/rag_logs/answer_api.log | python3 -c "
import sys, json
for line in sys.stdin:
try:
entry = json.loads(line)
if entry.get('negations'):
print(f\"{entry['timestamp']} | negations={entry['negations']} | {entry['query']}\")
except: pass
"When started via ./start_all.sh, service logs are also written to logs/ inside the project folder:
# View hybrid search API startup and request logs
tail -f logs/hybrid_api.log
# View answer API logs
tail -f logs/answer_api.log
# View React UI compilation output
tail -f logs/react_ui.logUse this quick checklist when ingesting a repo to ensure all stages completed successfully.
- Set
PROJECTconsistently iningest_commits.pyandcreate_patch_child_chunks.py. - Run ingestion stages in order:
python3 ingest_commits.py
python3 create_patch_child_chunks.py
python3 app/compute_embeddings.py- Verify DB rows and embeddings:
SELECT count(*) FROM chunks WHERE project='your-project-name';
SELECT count(*) FROM chunks WHERE project='your-project-name' AND embedding IS NOT NULL;- If embeddings are missing, re-run
app/compute_embeddings.pyand inspect console logs for provider errors or rate limiting. - For very large repositories, ingest in smaller
LIMITbatches and monitor Postgres disk usage.
-
UI shows no results: verify
hybrid_api.pyandanswer_api.pyare running andPOSTGRES_URLpoints at the right DB. -
Embedding dimension mismatch: ensure
PGVECTOR_DIMmatches the model (default384). If wrong, reinitialize the DB schema on a fresh database. -
pgvectorextension errors: enable extension withCREATE EXTENSION IF NOT EXISTS vector;as a superuser. -
LLM errors or rate limits: check
GOOGLE_API_KEYandGOOGLE_MODEL. Reduce concurrency or switch to a local test model (Ollama) for development. -
Query parser keeps asking follow-ups: provide more context (file or function) or tune the
EXTRACTION_PROMPTexamples inquery_understanding.py. -
AST parsing failures during chunking: some files may use syntax unsupported by the parser; inspect
create_patch_child_chunks.pylogs and skip problematic files temporarily. -
Want to run services individually for debugging? Examples:
# Start Postgres via docker-compose
docker compose up -d postgres
# Run hybrid API locally
python3 hybrid_api.py
# Run answer API locally
python3 answer_api.pyWe welcome contributions. Quick guide:
- Run tests locally with
pytest. - Tests are in the
tests/folder. To run a single test file:
pytest tests/test_query_understanding.py -q- Add a unit test for any behavior you change. Keep tests small and focused.
- Format Python with
black .and runflake8if installed. - To propose a change: fork, create a topic branch, add tests, open a PR with a short description and the failing case if applicable.
If you'd like, I can also add CI workflow examples (.github/workflows/python-app.yml) for running tests on PRs.