Skip to content

Repository files navigation

Debate·AI

Two LLMs argue. A third one judges. Live-streamed, voiced, scored.

Two minds — One topic — One verdict.

Python FastAPI React TypeScript Vite Tailwind LangChain

Setup screen


What it is

Debate·AI runs a multi-turn structured debate between two large language models on any topic you pick — one playing affirmative, the other negative. A third LLM watches the whole transcript, scores both sides, and delivers a written verdict.

The frontend streams every turn over Server-Sent Events as it's generated, voices each argument with neural TTS, and reveals the text word-by-word in lockstep with the audio so the on-screen reading matches what's actually being spoken.

Built as an exploration of multi-LLM orchestration, real-time streaming, and synchronized multimodal output in a single full-stack project.


Demo

1. Configure the debate — pick a topic, set turns (2–12), customize each debater's persona and stance.

Setup form

2. Watch it happen live — turns stream in over SSE, the active speaker glows, audio plays while text reveals in sync.

Debater A speaking

3. Watch the rebuttals — each debater rebuts the previous point before adding their own, in a strict alternating rotation.

Both debaters arguing

4. The judge delivers a verdict — structured scoring (1–10 each), a winner, and 2–4 sentences of reasoning.

Final verdict


What makes it interesting

Multi-provider LLM orchestration

The two debaters run on different model families on purpose so they sound genuinely different rather than two clones with different prompts:

Role Provider Model
Debater A Mistral AI mistral-small-2506
Debater B Groq llama-3.1-8b-instant
Judge Groq llama-3.1-8b-instant

Real-time streaming end-to-end

Every event — turn start, speaker change, full turn text, final verdict — is pushed to the frontend over Server-Sent Events as the generator yields it. No polling, no waiting for the whole debate to complete.

Synchronized voice + text reveal

The trickiest piece. When a turn arrives, the frontend:

  1. Fetches the audio blob from /api/tts (Microsoft Edge neural voices via edge-tts).
  2. Reads the audio's true duration after loadedmetadata.
  3. Reveals the transcript word-by-word over exactly that duration using requestAnimationFrame, so the on-screen text matches the voice you're hearing.
  4. Queues the next turn so debaters never talk over each other.

It also degrades gracefully — if TTS fails or the user hits mute, the reveal still runs at a natural pace (~3 words/sec) without audio.

Structured judging with Pydantic

The judge uses LangChain's with_structured_output(Verdict) so the verdict is never free-form text — it's a typed object with winner, debater_a_score, debater_b_score, and reasoning, validated by Pydantic before it leaves the backend.

Word-budget escalation

Each turn has a target length ([40, 70, 100, 130, 150, 150] by default) so debates ramp up — short opening jabs, longer rebuttals as the argument deepens.


Architecture

flowchart LR
    User((User)) -->|configure| UI[React + Vite UI]
    UI -->|POST /api/debate/stream| API[FastAPI]

    API --> Orchestrator[stream_debate generator]
    Orchestrator -->|alternates turns| ChainA[Debater A chain<br/>Mistral]
    Orchestrator --> ChainB[Debater B chain<br/>Groq Llama]
    Orchestrator -->|final transcript| Judge[Judge chain<br/>Groq + structured output]

    Orchestrator -.->|SSE: start, turn, verdict, done| UI
    UI -->|POST /api/tts per turn| TTS[edge-tts<br/>Aria + Guy voices]
    TTS -.->|audio/mpeg| UI
    UI -->|synced reveal| User
Loading

Tech stack

Backend — Python 3.10+, FastAPI, LangChain, langchain-mistralai, langchain-groq, Pydantic v2, Uvicorn (SSE streaming), edge-tts (neural TTS).

Frontend — React 18, TypeScript, Vite, Tailwind CSS, Framer Motion (animations), native fetch + ReadableStream for SSE parsing.

Testing — pytest + httpx (49 backend tests), Vitest + Testing Library + jsdom (42 frontend tests). Chains, edge-tts, and fetch are stubbed so the suite runs offline with zero API spend.


Quick start

Backend

python -m venv venv
venv\Scripts\activate              # Windows  (source venv/bin/activate on Unix)
pip install -r requirements.txt
cp .env.example .env               # add MISTRAL_API_KEY + GROQ_API_KEY
python server.py                   # runs on 127.0.0.1:8001 with hot reload

Port 8001 is the default. On Windows, port 8000 is often reserved by Hyper-V/WSL dynamic port ranges and produces a misleading WinError 10048. If 8001 is also blocked, change the port in server.py and the proxy target in frontend/vite.config.ts.

Frontend

cd frontend
npm install
npm run dev                        # http://localhost:5173

CLI (no frontend)

python main.py                              # random topic
python main.py -t "Should AI be regulated"  # custom topic
python main.py --list-topics

Testing

A full SQA suite covers both ends. No real LLM or TTS calls happen in CI — chains and edge_tts.Communicate are patched with deterministic fakes.

Backend — 49 tests

python -m pytest tests -v

Covers: config validation, Pydantic models, transcript helpers, stream_debate event ordering and error paths, run_debate CLI flow, FastAPI routes (/api/health, /api/defaults, /api/debate/stream happy + validation paths, /api/tts voice mapping + validation + 502 fallback), CORS preflight.

Frontend — 42 tests

cd frontend
npm test

Covers: SSE parser (chunked reads, malformed JSON, comment lines), useTTS hook (mute persistence, empty-text fast path, cancellation), every component (SetupForm, SpeechBubble, Debater, DebateArena, VerdictReveal), and the full App phase machine setup → debating → done.


Project layout

src/debateai/
  config.py      # topics, default budgets, personas, model IDs
  models.py      # Verdict pydantic + transcript types
  events.py      # streaming event TypedDicts
  prompts.py     # debater + judge prompt templates
  chains.py      # LLM init + chain builders
  debate.py      # stream_debate generator + run_debate CLI wrapper
  cli.py         # argparse entry
  api.py         # FastAPI app, SSE endpoint, TTS, CORS

frontend/src/
  App.tsx                    # phase machine: setup → debating → done
  types.ts
  lib/
    api.ts                   # SSE stream parser
    tts.ts                   # synced audio + word-by-word reveal
  components/
    SetupForm.tsx            # topic + personas + turns config
    DebateArena.tsx          # two-column layout, live transcript
    Debater.tsx              # animated Baymax avatar
    SpeechBubble.tsx         # framer-motion bubble
    VerdictReveal.tsx        # final verdict card

tests/                       # pytest suite (49 tests)
frontend/src/**/*.test.{ts,tsx}   # vitest suite (42 tests)

Customizable per debate

  • Topic — pick from built-in list or type your own
  • Number of turns — 2 to 12, slider
  • Each debater — display name, persona description, stance
  • Voice — Aria (US female) for A, Guy (US male) for B; mute toggle persists in localStorage

Roadmap

  • Shareable debate links (/debate/:id)
  • Persistent debate history
  • Streaming token-by-token output (currently per-turn)
  • Multi-model judge ensemble
  • User-selectable voices

Built with FastAPI, React, LangChain, Mistral, Groq, and edge-tts.

About

AI-powered debate system where multiple LLM agents argue different perspectives on a given topic, enabling structured, multi-round discussions with reasoning, rebuttals, and final conclusions.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages