| title | GraphRAG API |
|---|---|
| emoji | π§ |
| colorFrom | green |
| colorTo | blue |
| sdk | docker |
| app_port | 8080 |
| pinned | false |
GraphRAG entity retrieval was rewritten to match the actual TigerGraph schema and query the live graph directly (see
scripts/graphrag_queries.gsql); the numbers below are pending a fresh benchmark run against the corrected pipeline. The previous numbers quoted here (91.7% / 80.9% / 0.889 BERTScore) came from a broken retrieval path that silently fell back to a hand-built 31-entity snapshot instead of the live graph, and an API judge with auto-pass logic β neither reflected a genuine run. Runpython eval/evaluate.pyagainstdata/qa/qa_pairs.json(the real 75-question set) and replace this table with the actual output.
Dataset: 63,632 U.S. court opinions (legal case law) Β· 117.5M tokens (Gemini count_tokens verified) Β· 9,632 real citation edges
NOTE: this README predates the pivot from a Wikipedia corpus to a legal case-law corpus and still describes the old pipeline in places below (chunk counts, example questions, etc.) β treat the dataset stat line above and the Data Source & Licensing section as current; the rest is pending a full rewrite.
- Source: U.S. court opinions (state and federal), sourced via the Pile of Law dataset's
courtlistener_opinionssplit, which itself aggregates public CourtListener data. - Underlying content is public domain: judicial opinions authored by judges in their official capacity are not copyrightable in the United States (government edict doctrine, confirmed for state courts by Georgia v. Public.Resource.Org, 2020). The case-law text itself carries no copyright restriction.
- Pile of Law's specific compiled/curated dataset artifact is licensed
CC-BY-NC-SA-4.0(non-commercial, share-alike) β this applies to their packaging/selection of the data, not the underlying public-domain opinion text. This hackathon submission is non-commercial (research/competition use), consistent with that license. - Court/case metadata (case name, court, year) was extracted from each opinion's own citation header using eyecite (Free Law Project's own citation-parsing library β the same organization behind CourtListener), plus courts-db for reporter/court resolution.
- Citation graph edges were built from CourtListener's public
citation-mapbulk data (no auth required), filtered to edges where both the citing and cited opinion are present in this corpus.
Three RAG pipelines run in parallel on every question. The live React dashboard shows token counts, latency, cost, LLM-as-a-Judge verdicts, and BERTScore side-by-side.
Benchmark against the full 75-question set (data/qa/qa_pairs.json) against the live TigerGraph graph β reproduce with python eval/evaluate.py. (Table pending re-run β see note above.)
| Pipeline | Strategy | Avg Tokens | Avg Latency | Pass Rate |
|---|---|---|---|---|
| 1 β LLM-Only | Raw Gemini call, no retrieval | TBD | TBD | TBD |
| 2 β Basic RAG | FAISS top-8 β Gemini | TBD | TBD | TBD |
| 3 β GraphRAG | Entity seed + 1-hop TigerGraph ENTITY_COREF β Gemini | TBD | TBD | TBD |
Latency is kept low by a connection-pooled HTTP session (reuses one TLS connection across the ~18 graph calls), a startup warm-up (token/connection/thread-pools/Gemini primed in the background), and a per-entity cache (repeated questions β 1.2s).
100,850 Wikipedia articles (102.9M tokens)
β
βββ data/raw/dataset.jsonl β downloaded source
βββ data/chunks/chunks.pkl β 464,739 text chunks
βββ data/chunks/rag_index.faiss β FAISS L2 index
Query flow per pipeline:
Pipeline 1 β LLM-Only
Question βββΊ Gemini Flash βββΊ Answer
Pipeline 2 β Basic RAG
Question βββΊ FAISS (top_k=8, mmap) βββΊ 8 chunks as context βββΊ Gemini Flash βββΊ Answer
Pipeline 3 β GraphRAG [TigerGraph-powered]
Question βββΊ keyword/phrase candidates
βββΊ TigerGraph (single GSQL call, graphrag_retrieve):
β’ Entity.name match (case-insensitive) β seeds
β’ 1-hop ENTITY_COREF neighbour expansion
β’ returns each entity's `fact` attribute
Compact context (~180 tokens)
βββΊ Gemini Flash βββΊ Answer
API (FastAPI, port 8080): POST /compare β runs all 3 pipelines, returns JSON
Frontend (React, port 5173): live dashboard with charts, history, domain questions
graphrag-hackathon/
βββ api/
β βββ app.py # FastAPI β POST /compare, LLM judge, BERTScore
β βββ requirements.txt # Python dependencies
βββ data/
β βββ raw/dataset.jsonl # 100,850 Wikipedia articles (downloaded)
β βββ chunks/
β β βββ chunks.pkl # 464,739 text chunks
β β βββ rag_index.faiss # FAISS vector index for Pipeline 2
β βββ qa/qa_pairs.json # QA pairs for evaluation
βββ eval/
β βββ evaluate.py # full evaluation: tokens + latency + judge + BERTScore
β βββ results/eval_results.csv
βββ frontend/ # React + Recharts + Lucide dashboard
β βββ src/App.jsx # single-file UI
β βββ vite.config.js # proxies /compare β localhost:8080
βββ graphrag/ # TigerGraph GraphRAG Docker service
β βββ docker-compose.yml # graphrag + graphrag-ecc + chat-history
β βββ configs/server_config.json # Gemini keys, TigerGraph connection
βββ pipelines/
β βββ pipeline1_llm.py # LLM-only baseline
β βββ pipeline2_rag.py # FAISS RAG
β βββ pipeline3_graphrag.py # FAISS seed + TigerGraph hybrid
β βββ utils.py # Gemini client, token counter, cost helper
βββ scripts/
βββ download_dataset.py # fetch Wikipedia articles β data/raw/
βββ preprocess.py # chunk articles β data/chunks/chunks.pkl
βββ build_faiss.py # build FAISS index
βββ generate_qa_pairs.py # generate QA pairs from actual dataset
βββ prepare_graphrag_data.py
βββ init_graphrag_service.py # create GraphRAG schema on TigerGraph
βββ ingest_via_graphrag.py # push articles through GraphRAG service
βββ reingest_failed.py # re-ingest articles that failed
βββ count_tokens_gemini.py # token usage audit via Gemini API
See Guide.md for the full step-by-step setup.
# 1. Install dependencies
pip install -r api/requirements.txt
# 2. Set env vars
cp .env.example .env
# Edit .env β add your GEMINI_API_KEY
# 3. Build data (one-time, skip if data/ already exists)
python scripts/download_dataset.py
python scripts/preprocess.py
python scripts/build_faiss.py
# 4. Start API + frontend
uvicorn api.app:app --reload --port 8080 # Terminal 1
cd frontend && npm install && npm run dev # Terminal 2
# 5. Open http://localhost:5173For full GraphRAG with TigerGraph graph expansion, see Option B in Guide.md.
Create .env in graphrag-hackathon/:
GEMINI_API_KEY=AIza... # required β all 3 pipelines use Gemini Flash
GROQ_API_KEY=gsk_... # optional β not used in current pipelines
TG_HOST=https://...tgcloud.io # TigerGraph Savanna host (Option B only)
TG_USERNAME=your@email.com
TG_PASSWORD=your_tg_password
TG_GRAPH=MyDatabase
GRAPHRAG_URL=http://localhost:8000Runs all three pipelines and returns a comparison.
Request:
{
"question": "Who invented the telephone?",
"ground_truth": "Alexander Graham Bell"
}ground_truth is optional β include it to get LLM judge verdicts and BERTScore.
Response:
{
"llm_only": { "answer": "...", "total_tokens": 103, "latency_s": 0.8, "cost_usd": 0.000008 },
"basic_rag": { "answer": "...", "total_tokens": 1243, "latency_s": 13.1, "cost_usd": 0.000093 },
"graphrag": { "answer": "...", "total_tokens": 312, "latency_s": 5.2, "cost_usd": 0.000023,
"retriever": "faiss+tigergraph_hybrid" },
"token_reduction_pct": 74.9,
"judge_llm_only": "FAIL",
"judge_basic_rag": "PASS",
"judge_graphrag": "PASS",
"bertscore": { "raw_f1": 0.912, "rescaled_f1": 0.824, "bonus_hit": true }
}Returns {"status": "ok"}.
Returns FAISS/embedder load status.
Schema (built by scripts/add_entity_schema.py + scripts/extract_entities.py):
Article --HAS_CHUNK--> Chunk --HAS_ENTITY--> Entity(id, name, fact, chunk_id)
Entity <--ENTITY_COREF--> Entity
# From pipelines/pipeline3_graphrag.py
# 1. Extract candidate name/phrase strings from the question (uni/bi/tri-gram)
candidates = _extract_candidates(question)
# 2. Single GSQL call: match Entity.name (case-insensitive) -> seeds,
# then 1-hop ENTITY_COREF expansion -> same real-world entity in other chunks/docs
resp = requests.get(
f"{TG_HOST}/restpp/query/{GRAPH_NAME}/graphrag_retrieve",
headers={"Authorization": f"Bearer {token}"},
params={"candidateNames": candidates, "maxSeeds": 4, "maxNeighbors": 5},
)
entities = resp.json()["results"][0]["Result"] # [{name, fact, chunk_id, is_seed}, ...]The graphrag_retrieve query (scripts/graphrag_queries.gsql, installed via
scripts/install_graphrag_query.py) performs, server-side in one round-trip:
- Entity seeding β match candidate strings against
Entity.name(case-insensitive) - 1-hop traversal β
Entity -(ENTITY_COREF)- Entity, the same real-world entity mentioned in other chunks/documents - Compact fact retrieval β returns each matched entity's
factattribute, not raw chunk text
Context is capped at ~180 tokens, then Gemini synthesizes a concise answer using the same
300-token completion cap as Pipelines 1 and 2. Verify retrieval against the live graph
with python scripts/verify_graphrag_retrieval.py --qa-sample 10.
Honest reporting: if no seed entity exists in the graph, Pipeline 3 returns a
graph_context_found: falseflag and the API reportstoken_reduction_pct: nullβ a retrieval miss is never counted as a token "win". Seeapi/app.py.
| Criterion | Weight | Result |
|---|---|---|
| Token Reduction | 30% | Pending re-run of eval/evaluate.py against the corrected pipeline (see note at top) |
| Answer Accuracy | 30% | Pending re-run β judged with the single strict LLM-judge in eval/evaluate.py / api/app.py, no auto-pass |
| Performance | 20% | Pending re-run β connection pooling + warm-up retained; honest fast-fail when the graph is offline |
| Engineering | 20% | Live dashboard + TigerGraph retrieval against the real schema (Entity.name/fact, ENTITY_COREF) + honest, reproducible eval |
Model: Gemini 2.0 Flash ($0.075/1M input Β· $0.30/1M output)
| Operation | Est. Cost |
|---|---|
| Full dataset ingestion (one-time) | ~$0.01 |
| Per question (all 3 pipelines) | ~$0.001 |
| $40 budget | ~20,000β40,000 questions |