A fact-checked debate platform: users post a proposition and pro/con arguments; the backend extracts a claim, searches for evidence, and only then writes the row.
The API is FastAPI on PostgreSQL. The UI is Next.js 16 with Supabase Google OAuth. Claude Haiku plus Tavily score arguments 1–5; Claude Sonnet 4 reformulates propositions and synthesizes a summary, consensus view, and timeline once both sides exist.
Live site: https://debately-delta.vercel.app/ — hosted on Vercel.
- Proposition validation: Sonnet reformulates input into five policy, value, or fact statements and can reject incoherent or non-debatable topics
- Relevance gate: Haiku extracts a verifiable claim; arguments with no factual claim are rejected with HTTP 400 before insert
- Evidence search: Tavily
advancedsearch,max_results=10, keep sources with score > 0.5, pass the top 3 to scoring - Validity score: Haiku assigns 1–5 stars from source count and average Tavily score (5 requires average > 0.8 and 2–3 sources)
- Lazy synthesis:
GET /api/topics/{id}backfills missing scores, then asks Sonnet for overall summary, consensus, and timeline when both sides have arguments - Topic metrics: per-side average validity; controversy label once a topic has more than 6 arguments
- Votes and comments on arguments (authenticated)
- Per-user quota of 25 contributions (topics + arguments), checked before fact-checking
- Global cap of 750 Anthropic calls and 750 Tavily calls, stored in
api_usage
- Python 3.11 or 3.12 (pinned deps;
psycopg2-binary==2.9.9andtiktokenvia Tavily do not install cleanly on 3.13 without extra toolchains) - Node.js 18+
- PostgreSQL with SSL (
database.pysetssslmode='require') - Anthropic, Tavily, and Supabase credentials
git clone https://github.com/mdelriolanse/Debately.git
cd Debately/backend
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate backend/.env:
ANTHROPIC_API_KEY=
TAVILY_API_KEY=
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_ANON_KEY=
SUPABASE_JWT_SECRET=
DB_HOST=localhost
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASSWORD=config.py refuses to import without ANTHROPIC_API_KEY and TAVILY_API_KEY. Tables are created on first process start via database.init_db().
uvicorn main:app --reload --host 0.0.0.0 --port 8000The API listens on http://localhost:8000. GET /health checks the database.
Docker Compose (backend/docker-compose.yml) builds the same image and a Postgres 15 sidecar, published on 8080 (the image PORT default), not 8000. Point the frontend at http://localhost:8080 if you use Compose.
npm install and npm run build succeed in this tree. There is no test script. npm run lint is declared but eslint is not a dependency.
cd frontend
npm installCreate frontend/.env.local:
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=
NEXT_PUBLIC_API_URL=http://localhost:8000npm run devUI at http://localhost:3000. Production build:
npm run buildmain.py allows only https://debately-delta.vercel.app. Browser calls from localhost:3000 will fail CORS until that list is changed. The ALLOWED_ORIGINS name in older notes is not read by the app.
- Sign in with Google (Supabase OAuth).
- Open
/new, enter a topic; the API returns five reformulated propositions. - Submit at least one pro and one con argument. Irrelevant text is rejected before insert; accepted arguments store a 1–5 score and up to three source URLs.
- Open
/browsefor topic cards (counts, average validity, controversy). - Open
/topic/[id]for arguments sorted by score, votes, comments, and the Sonnet synthesis.
Example: create a topic (JWT required):
curl -X POST http://localhost:8000/api/topics \
-H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"proposition":"Remote work should be the default for office jobs.","created_by":"demo"}'backend/
main.py FastAPI app, CORS, health
config.py Immutable env + model ids
database.py Postgres schema and queries
fact_checker.py Claim extract → Tavily → 1–5 score
claude_service.py Sonnet summary / consensus / timeline
validate_proposition.py Sonnet proposition reformulation
routes/ topics, arguments, summaries, fact_checking, voting, auth
middleware/auth.py Supabase JWT
docker-compose.yml Backend + Postgres 15 on port 8080
frontend/
app/ Home, /browse, /new, /topic/[id], OAuth callback
src/api.ts Typed client (`NEXT_PUBLIC_API_URL`, default :8000)
lib/supabase/ Browser, server, and middleware clients
contexts/AuthContext.tsx
| Variable | Where | Notes |
|---|---|---|
ANTHROPIC_API_KEY |
backend | Required at import. Models: claude-sonnet-4-20250514, claude-3-haiku-20240307 |
TAVILY_API_KEY |
backend | Required at import |
SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_JWT_SECRET |
backend | JWT verification |
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD |
backend | Defaults: port 5432, name postgres, user postgres. SSL required |
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
frontend | Required by the Supabase clients |
NEXT_PUBLIC_API_URL |
frontend | Defaults to http://localhost:8000 |
Controversy (topics with more than 6 arguments): balance ratio = smaller side / total. >= 0.4 Highly Contested, >= 0.25 Moderately Contested, else Clear Consensus.
argument_matches exists in the schema with get/save helpers; no route currently writes matches.
Open a pull request against main. No license file is in the repository.