ย ย ๐ญ Where it came from ย ยทย ๐ What it does ย ยทย ๐ธ Guided tour ย ยทย โก Quick start ย ยทย ๐ API keys ย ยทย ๐ญ How the debate works ย ยทย ๐ Voice ย ยทย ๐ Config ย ยทย ๐งช Tests ย ยทย ๐ Troubleshooting
I've always been curious about the books I read. Some endings I just couldn't let go of โ I'd keep rewriting them in my head while I was working, while I was at the gym, while I was doing dishes โ and then at night they wouldn't let me sleep. Characters I'd known for years would show up in my head still going at each other, wondering out loud what they would have said if they'd got one more round on the stage.
WhatIfSabha is a side project born from that wondering. You upload a story, type a "what if", and watch the characters themselves argue it out โ hosted by Boru the Elephant, my personal AI companion on loan to this project as moderator. You won't get the book's actual ending. You'll get a conversation between AI versions of the characters, which is a different thing, and sometimes a more interesting one.
Why I chose Animal Farm. I'd read it more than a decade ago, and a few questions from it never stopped bothering me โ what if Snowball had come back, what if Boxer had refused the van, what if the animals had just walked away the night they saw the pigs on two legs. They were always there, those questions, running in the back of my head while I did other things, and every time I'd build a different theory to answer them โ one day I'd land on one perspective, a week later I'd be somewhere else entirely. I've had this conversation with friends many times โ they'd state their points, make their own theories, all of it. But I never settled. I don't think I'll settle with this app either โ but each run gives me a new angle, a new perspective, something I hadn't thought of. It just makes me think more. So Animal Farm was the obvious first thing to point this at, and it's been my test bench ever since โ if the moderator, the characters, the ledger all work on this one, they'll probably work on whatever book you throw at it next.
๐ This is a side project โ I work on it whenever I get some free time. It's rough in places and that's fine.
Upload a PDF. The app pulls out the characters, gives each of them a little brain and some context from the book, generates a portrait for each (via Pollinations), and then hands the floor to Boru โ the elephant moderator. You give Boru a divergence point โ the "what if" โ and the debate begins.
Boru runs the room: calls on people, forces confrontations between contradictions, drags silent characters in, closes the session when things have been said. A narrator writes a summary at the end based on everything that was argued.
The whole thing streams live over SSE, so you watch it unfold turn by turn rather than waiting for a block of text at the end.
A quick taste โ Animal Farm, with the divergence "What if Snowball returned?"
Boru: "Snowball walks back into the farm. Napoleon โ your move. Speak."
Napoleon: "Traitor. He sold us to Jones once, he'll sell us again..."
Boru: "Clover, you were there both nights. Which nose was in the feed-bin?"
Clover: "Napoleon's. I did not say so at the time because..."
Boru: "Snowball, answer the charge. Did you signal the humans?"
Snowball: "I signalled nothing. And Napoleon knows it..."
While this streams, a live interaction graph shows who's talking to whom, a quiet argument ledger tracks every open question, and before Boru closes the session, he runs a resolution round that forces answers to the biggest unanswered things.
You can step in too. Type your name and an interjection at any point โ Boru acknowledges you, decides whether to redirect or hand the floor to a specific character, and your line shows up in the transcript as an audience turn. Off-topic / rude messages get politely shut down (Boru is patient but not a doormat).
After the debate: the world persists. You can walk up to any character and ask them questions โ Oracle mode โ and they answer from inside the alternate reality the debate shaped.
๐ See the demo: https://whatif-sabha.pages.dev
Bundled demo: Animal Farm with the divergence "What if Boxer killed those dogs when they were trying to chase Snowball away?" โ 44 turns, 17 characters, full graph + ledger + Boru's notes timeline + closing + summary.
Drop a PDF. The app pulls out the characters, writes a short dossier for each, and generates a portrait per character (via Pollinations).
The cast is clickable, and a short generated timeline of the original story gives the starting reality. You can also see detailed fair witness analysis on each character through internet and wiki research.
Before picking a what-if, you can chat with the orchestrator about the book โ he'll reason across the whole cast and remember the conversation.
Each character is queryable individually, grounded only in what that character would know.
Boru hosts. Characters argue. Ledger fills in real time. Graph updates per turn.
Type your name once at the start, then drop questions or comments into the same Sabha at any point. Boru reads them in-character โ sometimes nodding you toward a specific speaker ("Clover, the audience asks you directly"), sometimes redirecting you back to the topic if you've wandered, sometimes shutting down a hostile interjection ("This is a Sabha, friend. We use words, not weapons."). Your line lands in the transcript as an audience turn alongside the characters'.
Force-directed. Arrows styled by speech act (response vs question). Drag any node to pin it. Click a node to spotlight its outgoing arrows.
After the debate closes, the world persists. Any character will answer you from inside the reality the debate shaped. Per-character history is kept for the session โ switch between characters and come back without losing context.
๐ฏ You need: Python 3.10โ3.12 ยท Node 20+ ยท a free Gemini API key (under 30 sec โ grab one at aistudio.google.com/apikey). Other providers are supported as add-ons but Gemini is the recommended primary because of its 1M context window โ see API keys for the details and trade-offs.
(Python 3.13 isn't supported yet โ
chromadb 0.5.23and a couple of other deps haven't published 3.13 wheels. If you only have 3.13 installed, usepyenvoruvto grab 3.12 alongside it.)
The simplest path: start the app, then paste your API key into the in-app settings panel โ no config files to edit.
git clone https://github.com/wadekarg/whatif-sabha.git whatif-sabha
cd whatif-sabha
./run.shThat's it. The script:
- Checks your prereqs (Python โฅ 3.10, Node โฅ 18, npm, git)
- Installs all dependencies (first run takes a few minutes)
- Starts both backend and frontend with prefixed real-time logs
- Opens your browser to http://localhost:3010
Re-running is near-instant if dependencies haven't changed.
If Python 3.10+ or Node 18+ aren't installed, the script will install them automatically (asking for your sudo password where needed). Pass --no-auto-install if you'd rather it just print the install commands and let you handle it manually.
Flags:
--reinstallโ force re-run of pip and npm install (use aftergit pullbrings new deps)--no-openโ don't open the browser automatically--no-auto-installโ don't auto-install missing prereqs; just print install commands--helpโ show flag reference
Native Windows isn't supported. Install WSL, then open a WSL terminal and run the Easiest steps above.
git clone https://github.com/wadekarg/whatif-sabha.git whatif-sabha
cd whatif-sabha
cp backend/.env.example backend/.env # creates an empty .env (no editing needed)
docker compose upThen open http://localhost:3010, click the โ๏ธ gear icon in the top-right, paste your free Gemini key (or any other supported provider), save, and you're done. Upload a PDF, type a what-if, watch it debate.
Two terminals โ one for the backend, one for the frontend.
Terminal 1 โ Backend:
cd backend
python -m venv venv
source venv/bin/activate # on Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # creates an empty .env (no editing needed)
uvicorn app.main:app --port 8001 --reloadTerminal 2 โ Frontend:
cd frontend
npm install
npm run devThen open http://localhost:3010, click the โ๏ธ gear icon in the top-right, paste your free Gemini key, save. Done.
๐ก Why the gear icon, not
.env? The in-app settings store keys in the backend's runtime DB and let you pick models live per provider. It also works for non-developers without editing files. The.envroute is still supported โ see the Config reference below โ and is recommended for headless deployments and CI.
TL;DR: A free Gemini key handles everything. You can mix and match other providers (Cerebras, Groq, NVIDIA, Anthropic, OpenAI, your own endpoint) for speed/quality on specific roles, but if you only get one key, make it Gemini.
The pipeline has steps that need to ingest the whole book in one shot โ story-structure analysis, theme extraction, divergence-point discovery. That demands a large context window. Here's what each provider gives you:
| Provider | Free? | Context window | Realistic for full-book ingest? |
|---|---|---|---|
| Gemini | โ 1500 req/day, 15 RPM | ~1,000,000 tokens | โ Yes โ any book, including the Mahabharata |
| Anthropic (Claude) | ๐ณ paid | ~200,000 tokens | โ Most novels |
| OpenAI (GPT-4o) | ๐ณ paid | ~128,000 tokens | โ Most novels |
| NVIDIA NIM | โ free tier | ~128,000 tokens | โ Most novels |
| Groq | โ free tier | ~32,000 tokens | โ Short books only (โค25k words) |
| Cerebras | โ free tier | ~8,000 tokens | โ Excerpts only (โค6k words) |
| Bring-your-own (DeepSeek, OpenRouter, Ollama, etc.) | varies | varies | check the provider's spec |
The honest gotcha: if you set up only Cerebras (or only Groq) and try to upload a full book, the analysis step will fail with a context-length error. Cerebras and Groq are excellent for the fast turn-by-turn character voices during the debate (they're literally the fastest LLM inference available), but they can't swallow a whole novel for analysis. We use them for what they're good at and lean on Gemini / Anthropic / OpenAI / NVIDIA for the long-context steps.
For completely free, full-book support:
Gemini key โ handles long-context analysis + character extraction
+ Cerebras key โ handles fast character voices during the live debate
Both have generous free tiers. The router automatically uses Gemini for analysis and Cerebras for character turns. You won't hit rate limits on hobby use.
For a single-key minimum: just Gemini. Slower on the debate (Gemini ~10 tok/sec vs Cerebras ~1000 tok/sec), but it works end-to-end on any book size.
For paid-key users: add Anthropic (best quality), OpenAI (best long-form prose). Router chains them as fallbacks for rate-limit resilience.
You will not hit these on hobby use. For reference:
| Limit | Free tier | Typical book upload |
|---|---|---|
| RPM (requests/minute) | 15 | ~5-8 calls |
| TPM (tokens/minute) | 1,000,000 | ~50K-200K |
| RPD (requests/day) | 1,500 | ~10-30 |
| Provider | Where | Time |
|---|---|---|
| Gemini | aistudio.google.com/apikey | 30 sec |
| Cerebras | cloud.cerebras.ai | 1 min |
| Groq | console.groq.com/keys | 1 min |
| NVIDIA NIM | build.nvidia.com | 2 min |
| Anthropic | console.anthropic.com/settings/keys | 1 min (paid) |
| OpenAI | platform.openai.com/api-keys | 1 min (paid) |
Anything that exposes an OpenAI-compatible /chat/completions endpoint works through a single generic slot in the gear icon โ three fields: base URL, API key, model name. This unlocks:
DeepSeek ยท Qwen / Dashscope ยท Kimi (Moonshot) ยท Zhipu GLM ยท OpenRouter ยท Together ยท Fireworks ยท Perplexity ยท Ollama / LM Studio (local) ยท Azure OpenAI ยท GitHub Models ยท vLLM ยท llama.cpp โ basically any LLM provider, plus fully-offline self-hosted setups.
The same context-window caveat applies: pick a provider whose serving model has a context window โฅ your typical PDF size.
Click the โ gear icon in the top-right. For each provider you've added a key for, the modal:
- Fetches the live list of chat-capable models (calls each provider's real
/v1/modelsAPI โ no hardcoded model lists in the code). - Shows a dropdown with our blessed pick marked
(recommended). First-time users save and go. - Has an Advanced collapsible per provider โ set different models for
Character voice / Story chat / Debate moderator / Narrator-summaryif you want fine-grained routing.
Each provider input also has a Clear key button if you want to remove a key (e.g., a revoked or rate-limited one) โ clearing actively overrides any leftover value in .env.
Picks persist in your browser's localStorage and on the backend's runtime settings. As providers ship new models, they appear in the dropdown automatically.
Add more than one key and the router builds a fallback chain per role. On rate-limit / quota / context-length errors, it falls through to the next provider that has a model configured for that role. So Gemini + Cerebras gives you Gemini's robustness for analysis with Cerebras's speed for voices, with each backing up the other if one rate-limits.
Boru is the interesting part. He's not just picking the next speaker by score โ he's actually enforcing a conversation. A short tour of what's in the code:
- ๐๏ธ Pending-invitee enforcement โ when Boru names someone (
"Napoleon โ your move"), that character is the next speaker. Heuristic scoring is skipped for the turn. Boru's word is law. - ๐ฃ๏ธ Vocative routing โ phrases like
"Mrs. Jones, speak"or"for Mrs. Jones"are parsed out of Boru's prose, so the intended speaker actually gets the floor. Works for character-to-character questions too: if A asks B something directly, B is pinned for the next turn. - โ๏ธ Dispute lifecycle โ the
ArgumentLedgertracks claim-vs-claim contradictions, escalates them through a couple of confrontation rounds, and then retires them with a pair cooldown so the same two people aren't shoved back into combat forever. Stale disputes auto-retire after ~10 untouched turns. - ๐ Silent rotation โ if the cast is going quiet (40%+ haven't spoken, or three-plus have never spoken), Boru actively pulls silent characters in. The pull gets stronger the longer someone stays frozen out.
- ๐ Pair-duel breaker โ after five exchanges dominated by the same two voices, a third voice is forced in to break the ping-pong.
- ๐ค Audience interjection โ you can interrupt mid-debate from the right-hand panel: type your name once, then drop questions or comments into the Sabha at any point. Boru handles them in-character: routes on-topic questions to the right character, redirects when you wander off-topic, calmly shuts down hostility. Your turn lands in the transcript labeled as audience.
- ๐ World observers โ 3โ4 real-world voices chosen per debate by tag-overlap with your divergence (for Animal Farm: a Soviet propagandist, a Trotskyist exile, a Ukrainian farmer under collectivization, a Cold War strategist) break in every 3โ4 turns with historical context the characters themselves can't see.
- ๐ Power Interrogator โ a structural voice that fires once at the midpoint. Not moral. One question: who benefits if this version of events is accepted as real? Names the interested party, asks the character with the most to gain from being believed, walks off.
- ๐ช Phase progression โ
opening โ cross_examination โ deepening โ reckoning โ closing, driven by the ledger's state rather than raw round count. - ๐ฏ Resolution round โ before the closing, Boru forces answers to the top still-open questions.
- ๐ป Ghost-speak for dead characters โ if your divergence says "kill Napoleon", Napoleon can still appear in the debate but speaks from the grave (past/conditional tense, foretells what the living will do) instead of giving active orders.
- ๐ Anti-repetition โ Boru's last few openers are injected into his own prompt as a "don't start with these" list. Dispute subjects get diversified.
- ๐ Hard stop after closing โ once the closing is delivered, no late stage directions slip through.
This whole layer is covered by tests in backend/tests/ (~90 tests across test_sabha_orchestrator_return.py, test_orchestrator_picker.py, test_dispute_retirement.py, test_reentry_logic.py, test_intended_speaker_parsing.py, test_boru_anti_repetition.py, test_character_speech_act.py).
Each character speaks in their own voice as the debate streams. Audio is generated on the fly by Edge TTS โ Microsoft's neural voice pool exposed through a free, keyless API โ and played turn-by-turn in the browser. No API key, no per-turn paid provider.
Voices aren't random. During upload, each character is scored against a library of ~130 trait keywords across three dimensions:
- ๐ฅ Energy โ how fast the character speaks. Paranoid ยท young ยท impulsive push it up; stoic ยท weary ยท grieving pull it down.
- ๐ Authority โ how deep the pitch sits. Commanding ยท sage ยท ruthless push deeper; innocent ยท cowardly ยท timid pull higher.
- ๐ข Presence โ how loud and projected. Theatrical ยท defiant ยท aggressive fill the room; melancholy ยท broken ยท aloof fade back.
The scorer reads every personality field the app has on a character โ description, role, phase traits, motivations, fears, fair-witness consensus, narrative bias โ then picks a base voice from a gendered pool of 16 Edge TTS voices and tunes rate / pitch / volume accordingly. Boru is fixed โ warm, authoritative, a little slow. Napoleon comes out deep and loud; Boxer steady and workmanlike; Mollie fluttery and quick.
Voices shift with emotion. On top of the baseline, each turn is classified (anger, grief, pride, guilt, defiance, betrayal, contempt, cold fury, hope, wearinessโฆ) and the speaker's rate / pitch / volume are modulated for that turn. Same character, same voice, but an angry Napoleon is faster and louder than a scheming Napoleon โ and a grieving Clover slows right down.
In the UI: every transcript line has a โถ button and the debate page auto-plays by default. Toggle ๐ Auto-Play On / ๐ Auto-Play Off from the header. Boru reads the closing summary via its own audio button on the summary card. Audio is cached per turn on the backend, so replaying a finished debate is instant.
Pronunciation patches. A few words that Edge TTS mishandles get phonetic swaps for TTS audio only โ e.g. sabha โ sabhaa to get the long Sanskrit vowel right. The on-screen transcript is never changed.
Relevant code:
backend/app/core/tts.py(profile scoring, emotion modifiers, generation),backend/app/api/routes/debate.py(/debates/{id}/tts,/debates/{id}/voices,/debates/{id}/tts/summary), and the auto-play queue infrontend/app/story/[id]/debate/page.tsx.
Once the debate ends, a few things happen:
- ๐ Summary narrator โ synthesizes a prose summary using the full ledger, not just the transcript tail.
- ๐ฎ Oracle Q&A โ keep asking any character from the alternate timeline questions; they answer in-character, grounded in what was argued. Per-character history persists for the session.
- ๐ Live interaction graph โ force-directed, updating per turn. Arrows styled by speech act (question vs response vs statement).
- ๐ Boru's notes timeline โ his progress notes from every round, kept as a history you can scroll through.
- ๐ PDF export โ builds a bound PDF with: title page, the real D3 graph (not a synthetic stand-in), cast strip, full transcript, ledger page with notes + open questions + claims, positions page, and the summary.
- ๐ Static replay site โ the
replay/subproject is a Next.js static export with its own multi-page tour (story ยท characters ยท debate replay) and pre-rendered Edge TTS audio bundled in, so the hosted demo plays voices without any backend. Deploy to Cloudflare Pages for a zero-backend shareable replay.
Everything lives in backend/.env (see backend/.env.example). At minimum a Gemini key (free) is recommended for full-book support; additional keys add fallback resilience and per-role routing โ see the API keys section for context-window trade-offs by provider.
| Variable | Purpose | Context | Free? |
|---|---|---|---|
GEMINI_API_KEY |
๐ Recommended primary โ handles long-context analysis | ~1M | โ |
CEREBRAS_API_KEY |
โก Ultra-fast character voices during the live debate | ~8K | โ |
GROQ_API_KEY |
๐ Fast inference, good for character voices and short tasks | ~32K | โ |
NVIDIA_API_KEY |
๐ฏ NVIDIA NIM โ broad model selection, decent context | ~128K | โ |
ANTHROPIC_API_KEY |
๐ณ Best quality (Claude) | ~200K | ๐ณ |
OPENAI_API_KEY |
๐ณ GPT-4o family | ~128K | ๐ณ |
CUSTOM_LLM_BASE_URL / CUSTOM_LLM_API_KEY / CUSTOM_LLM_MODEL |
๐ Bring your own โ any OpenAI-compatible endpoint (DeepSeek, Qwen, Kimi, OpenRouter, Ollama, LM Studio, Azure OpenAIโฆ) | varies | varies |
Heads-up on context-windows: Cerebras (~8K) and Groq (~32K) are excellent at fast turn-by-turn character voices but cannot ingest a full novel for the analysis step. If you only have one of those keys, upload will fail with a context-length error. Pair them with Gemini (or any other long-context provider) for full-book support. See the API keys section for details.
Per-provider env-var overrides for users who'd rather not click. The gear-modal pick takes precedence over these if both are set.
| Variable | Purpose |
|---|---|
ANTHROPIC_MODEL / OPENAI_MODEL / GEMINI_MODEL / GROQ_MODEL / CEREBRAS_MODEL / NVIDIA_MODEL |
Pin a specific model for that provider across all roles |
| Variable | Default | Purpose |
|---|---|---|
DATABASE_URL |
sqlite+aiosqlite:///./whatif_sabha.db |
Persistence |
UPLOAD_DIR |
./uploads |
PDFs + generated portraits |
MAX_UPLOAD_SIZE_MB |
50 |
Upload cap |
ALLOWED_ORIGINS |
localhost:3000, localhost:3001, localhost:3010 |
CORS |
ANALYSIS_MODEL / CHARACTER_AGENT_MODEL / JUDGE_MODEL / NARRATOR_MODEL |
(set in .env.example) | Legacy role-based model env vars โ preserved for backwards compat |
NEO4J_URI / NEO4J_USER / NEO4J_PASSWORD |
โ | Enables Graphiti character memory |
ENABLE_LIGHTRAG |
off | Narrative causal graph at upload (~60s) |
REDIS_URL |
โ | Optional caching layer |
| Variable | Default | Purpose |
|---|---|---|
NEXT_PUBLIC_API_URL |
http://localhost:8001 |
Backend URL |
- ๐ Backend โ Python 3.10+, FastAPI, uvicorn, SSE streaming
- โ๏ธ Frontend โ Next.js 16, TypeScript, Tailwind 4, D3 / force-graph
- ๐พ State โ SQLite (debates, turns, characters) + ChromaDB (per-character RAG over story text). Optional Graphiti + Kuzu for persistent character "soul memory".
- ๐ค LLMs โ a provider router across Gemini ยท Cerebras ยท NVIDIA NIM ยท Groq ยท Anthropic ยท OpenAI plus a bring-your-own slot for any OpenAI-compatible endpoint (DeepSeek, Qwen, Kimi, OpenRouter, Ollama, LM Studio, Azure OpenAIโฆ). Model picks per provider come from a live
/v1/modelsdropdown in the gear modal โ no hardcoded model ids in the code, the list updates as providers ship new models. Role-based routing (character / judge / narrator / analysis), per-role overrides under "Advanced", automatic failover on rate-limit / quota. - ๐ผ๏ธ Character portraits โ generated via Pollinations during upload. Free, no API key. Generation is best-effort โ a few portraits may not come through on any given upload; those characters fall back to an initials avatar.
- ๐ Voice โ free, keyless TTS via Microsoft Edge (
edge-tts). 16-voice pool, personality-driven base assignment across energy/authority/presence dimensions, emotion-driven per-turn modulation, audio cached to disk per turn.
Subprojects:
| Folder | What it is |
|---|---|
backend/ |
FastAPI app, debate engine, agents, routes, persistence |
frontend/ |
Next.js app โ upload, story pages, live debate, graph, PDF export |
replay/ |
Separate Next.js static export for hosted replays (Cloudflare Pages) |
demo/ |
Standalone HTML demo page |
cd backend
source venv/bin/activate
pytest tests/ -v
# Replay-site tests
cd ../replay && npm test
../backend/venv/bin/pytest scripts/test_export_debate.pyThe backend suite covers the moderator/picker logic โ re-entry triggers, dispute lifecycle, vocative parsing, anti-repetition, speech-act classification, Boru return paths. The content agents (character / narrator / judge) are verified by eye, not by unit tests.
| Story | Cast | Example divergences |
|---|---|---|
| ๐ท Animal Farm | Napoleon, Snowball, Boxer, Squealer, Clover, Benjamin, Mr. Jones | "What if Snowball returned?" ยท "What if the pigs stayed honest?" |
| ๐ก Hamlet | Hamlet, Claudius, Gertrude, Ophelia, Horatio | "What if Hamlet acted on the ghost immediately?" |
| ๐ Any PDF | Auto-extracted | You write the divergence. |
The multi-pass character extractor handles longer PDFs by chunking, so you can try your own books โ just be mindful of the copyright note below.
The debate seems stuck or isn't streaming
- Check that the backend is running on
localhost:8001. Open http://localhost:8001/health โ you should see{"status":"ok"}. - Check the browser console for SSE/EventSource errors โ usually a CORS or network issue.
- If you're on a corporate/VPN network, SSE connections can hang. Try a different network.
"Rate limit" errors mid-debate
- You're hitting the free-tier minute cap of your provider. Two options:
- Add a second API key from another provider to
.envโ the router will auto-failover. - Wait a minute and retry the debate.
- Add a second API key from another provider to
The character cast extraction is missing someone
- The analyzer uses a multi-pass chunking strategy. Very long books with many minor characters can drop the long tail.
- You can also edit
backend/whatif_sabha.dbdirectly or re-upload a smaller PDF covering a specific section.
PDF export looks blank or ugly
- The exporter captures the live SVG graph โ make sure the Graph tab is visible when you click Export.
- If you see "html2canvas failed" in the console, it's likely a CORS issue with a portrait image URL.
Reset everything
cd backend
rm whatif_sabha.db
rm -rf chroma_db uploadsNukes the database, the per-character embeddings, and any uploaded PDFs. Start fresh.
Frontend dev server is unresponsive / hung
Turbopack sometimes gets into a bad state. Fix:
cd frontend
rm -rf .next
npm run devnpm install fails with EBADPLATFORM on macOS or Windows
You're seeing something like:
npm error notsup Unsupported platform for @tailwindcss/oxide-linux-x64-gnu@4.x.x:
wanted {"os":"linux"} (current: {"os":"darwin"})
This happens when an old package-lock.json from a Linux machine sneaks into your clone and pins the Linux-only Tailwind native binding. The repo no longer commits package-lock.json for this exact reason โ but if you've cloned an older revision, regenerate:
cd frontend # or `cd replay` if it's there
rm -rf node_modules package-lock.json
npm installnpm install regenerates the lock file with the correct platform binary for your machine.
pip install fails with Could not find a version that satisfies the requirement pysqlite3-binary or chromadb errors
Two likely causes:
1. Python 3.13 โ neither chromadb 0.5.23 nor pysqlite3-binary ship wheels for 3.13 yet. Use Python 3.10, 3.11, or 3.12 instead. With pyenv:
pyenv install 3.12
pyenv local 3.12
python -m venv venv
source venv/bin/activate
pip install -r requirements.txtOr with uv:
uv venv --python 3.12
source .venv/bin/activate
uv pip install -r requirements.txt2. pip is out of date โ the conditional install marker (sys_platform == "linux") needs a recent pip. Upgrade first:
pip install --upgrade pip
pip install -r requirements.txtpysqlite3-binary is only needed on older Linux distros to override the system sqlite3 for ChromaDB. macOS and Windows have a new-enough sqlite3 by default โ the requirements file already skips it on those platforms. The backend code falls back gracefully if it's missing.
Keeping this honest โ it's a side project, not a product.
- Not every story has been tested end-to-end. Tuning has focused on short works like Animal Farm and Hamlet. Longer books may surface rough edges in extraction or pacing.
- No authentication. Single-user prototype โ don't put it on the public internet as-is.
- SQLite + ChromaDB on disk. No multi-tenancy. Resetting means clearing
backend/whatif_sabha.db,backend/chroma_db/, andbackend/uploads/. - AI-synthetic default fixtures. Bring your own PDFs for real runs.
- Rate limits on free tiers. Depending on which provider you're using, a long debate can bump into per-minute limits. The router falls back to other configured providers automatically, but with only one key configured you may see slowdowns.
Everything a debate produces is AI-generated. The characters you see arguing are language models role-playing based on the PDF you upload โ nothing they say appears in the source book, and none of it should be quoted as the author's words.
The demo debates in this repo were tested on George Orwell's Animal Farm, which entered the public domain in 2021 (US / UK / most EU jurisdictions after Orwell's life + 70 years). If you want to try the app on a book, please stick to works that are public domain or that you have permission to use. WhatIfSabha doesn't ship with any book โ you upload your own PDF.
If you believe this project uses something it shouldn't, open an issue and I'll address it.
I work on this in spare time, between other things. It started as a small curiosity โ what if the characters themselves could argue about an alternate ending? โ and it's grown in the directions I've been curious about on whatever given weekend. That means some parts are very polished (the moderator, the dispute ledger, the test suite around them) and some parts are rough (narrator prose, UI polish in places, docs). Issues and ideas are very welcome. If something breaks, drop the story, divergence, and provider combo in the issue โ that's usually enough to reproduce.
If you try it with a book you love and something surprising happens, I'd genuinely love to hear about it.
MIT License โ see LICENSE. Copyright (c) 2026 Gajanan Wadekar.















