Native Shell
Native Shell: AI-generated UI, native in your app, on every platform. Apps on iOS, Android, macOS, Windows and Linux, GPUI apps and web pages load signed, sandboxed TSX modules at run time and draw th
Open-source RAG evaluation: claim-level LLM-as-judge scoring, retrieval metrics and paired significance tests, with a live model arena.
Developer toolsTypeScript · CSSGitHub @s28066226-maxOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
RAG evaluation with claim-level LLM-as-judge scoring, retrieval metrics and significance tests. Live model arena.
Taken from the repository README
Loading the full README.
Read the full README ↗Find out whether your RAG model is actually better, and how sure you can be. Runs entirely on free APIs.
EvalForge runs the same retrieval-augmented question set against several LLMs and grades every answer with an LLM judge at the claim level. It then runs paired significance tests, so "model B is better" comes with a confidence interval and a p-value, not just a higher average.
It works with free providers out of the box: Groq (default), Google Gemini, OpenRouter free models, or fully local Ollama. No credit card is required.
The web app has a live arena: ask your own question, watch two models stream answers from the same retrieved documents, and see the judge fact-check every claim in real time.
A real run on Groq's free tier: gpt-oss-20b vs qwen3.8-27b, judged by gpt-oss-120b.
Most RAG comparisons look like "Model A: 87%, Model B: 91%, ship B." With 24 test questions, that 4-point gap is often noise. EvalForge answers three questions:
retry-after on HTTP 429, low default concurrency, low-effort judge reasoning to save tokens, and --limit for quick runs.data/published.json snapshot in production, so it runs on Vercel's free tier with no database to host.--fail-on-regression exits non-zero when any model is significantly worse than the baseline.questions.jsonl ─┐
├─► BM25 retrieve top-k ─► same context to every model
docs/*.md ───────┘ │
▼
┌──── answer with each model (gpt-oss-20b / qwen3.8-27b / …)
▼
LLM judge (strict JSON schema)
claims[] ✓/✗ · relevance · correctness · abstained
│
▼
SQLite (node:sqlite) ─► paired stats ─► CLI table + dashboard
Faithfulness = supported claims ÷ total claims for each answer. An answer that only abstains makes no claims, so it can't hallucinate. Hallucination rate = share of answers with at least one unsupported claim.
Requires Node.js 24+.
git clone https://github.com/<you>/evalforge && cd evalforge
npm install
# Offline demo, no key needed
npm run eval -- --mock
# Real evaluation on Groq's free tier (get a key at https://console.groq.com/keys, no card needed)
cp .env.example .env # then set GROQ_API_KEY
npm run eval
# Explore the results
npm run dev # http://localhost:3000
By default, two Groq models (openai/gpt-oss-20b, qwen/qwen3.8-27b) answer the 24 sample questions, and openai/gpt-oss-120b judges. Each Groq model has its own free rate limit. The judge is the bottleneck, so a full run takes about 5 minutes on the free tier. Use --limit 8 for a quick try.
Name every model <provider>:<model id>:
| Provider | Example | Key | Notes |
|---|---|---|---|
| Groq | groq:qwen/qwen3.8-27b | GROQ_API_KEY | Free, no card. Very fast. Per-model rate limits. |
| Gemini | gemini:gemini-3.8-flash | GEMINI_API_KEY | Free tier in AI Studio; check there for which models are free. |
| OpenRouter | openrouter:<model>:free | OPENROUTER_API_KEY | Models ending in :free cost nothing. |
| Ollama | ollama:gemma4:e4b | none | Runs on your machine, so it's unlimited and private. |
You can mix providers in one run, for example local models under test with a stronger free cloud judge:
npm run eval -- --models ollama:gemma4:e4b,groq:qwen/qwen3.8-27b --judge groq:openai/gpt-oss-120b
Choosing a judge: use the strongest model you have access to. Prefer a judge from a different family than the models under test, since LLM judges tend to favor their own family's answers.
npm run eval -- [options]
--dataset <dir> dataset directory (default: datasets/northwind)
--models <a,b,c> models as <provider>:<model id> (default: two Groq free-tier models)
--baseline <model> model the others are compared against (default: first model)
--judge <model> LLM judge (default: groq:openai/gpt-oss-120b)
--top-k <n> chunks retrieved per question (default: 4)
--effort <level> reasoning effort for reasoning models: low | medium | high (default: medium)
--concurrency <n> parallel requests (default: 1, to stay inside free-tier rate limits)
--limit <n> evaluate only the first n questions
--mock offline deterministic mode (automatic when a needed key is unset)
--fail-on-regression exit 1 if any model is significantly worse than the baseline
datasets/my-product/
├── docs/ # .md or .txt files: the retrieval corpus
│ ├── onboarding.md
│ └── billing.md
└── questions.jsonl
{"id": "q1", "question": "How long is the free trial?", "reference": "14 days.", "sources": ["onboarding"]}
{"id": "q2", "question": "Who founded the company?", "unanswerable": true}
reference is optional (correctness is skipped without one). Mark a question unanswerable to test whether models admit they don't know. sources lists the document file names (without extension) that contain the answer and turns on the retrieval metrics.
The dashboard and arena deploy to Vercel's free tier as a normal Next.js app.
Choose which runs the site shows and commit the snapshot:
npm run export # your latest completed live run
npm run export -- <run id> # or specific runs (add --only to replace the published set)
git add data/published.json && git commit -m "Publish results"
Push to GitHub and import the repo at vercel.com/new. No build settings need changing.
Optional environment variables in the Vercel project:
| Variable | Purpose |
|---|---|
GROQ_API_KEY | Makes the arena call real models. Without it the arena runs in mock mode. |
NEXT_PUBLIC_GITHUB_URL | Shows a GitHub link in the header. |
ARENA_MODELS, ARENA_JUDGE | Override the arena's models (same <provider>:<model> format). |
ARENA_RATE_LIMIT | Arena questions per IP per 10 minutes (default 6). |
ARENA_DISABLED=1 | Turns the arena off. |
The arena's rate limit is kept in memory per server instance. It protects a free quota from casual overuse, not from a determined attacker; the provider's own limits are the hard cap.
reasoning_effort, <think> tags) are handled in one place, and each is covered by tests against a fake server.src/
├── cli/
│ ├── eval.ts # run an evaluation
│ └── export.ts # publish runs to data/published.json for deployment
├── lib/
│ ├── stats.ts # Wilcoxon, t-test, bootstrap, Holm (+ tests vs SciPy)
│ ├── retrieval.ts # chunking, stemming, BM25 (+ tests)
│ ├── providers.ts # free-tier providers, LLM judge, mock provider (+ tests)
│ ├── prompts.ts # answer and judge prompts
│ ├── runner.ts # retrieve → answer → judge pipeline
│ ├── summary.ts # per-model metrics, comparisons, retrieval quality
│ ├── db.ts # SQLite persistence
│ ├── store.ts # reads SQLite locally, the published snapshot when deployed
│ └── arena.ts # arena config and streamed event types
└── app/
├── page.tsx # landing page with the latest leaderboard
├── runs/ # run list, run report, per-question drill-down
├── arena/ # live side-by-side arena (client streaming UI)
├── api/arena/ # NDJSON streaming endpoint: retrieve → answer → judge
└── methodology/ # every metric and statistical choice explained
datasets/northwind/ # sample corpus: fictional SaaS handbook
npm test # stats vs SciPy, retrieval recall, provider behavior against a fake API server
npm run typecheck
MIT