Skip to content

Repository files navigation

Whitespace

Whitespace monitors AI research papers and uses an LLM pipeline to surface novel, feasible startup ideas hiding in the gaps between papers. Every run produces a ranked set of ideas with novelty and feasibility scores, detailed breakdowns, and a "product sketch" you can generate on demand.

Papers are fetched from arXiv, which is where leading AI research organisations — Google DeepMind, Anthropic, OpenAI, Meta AI, Mistral, and others — publish the majority of their work. Whitespace searches arXiv using configurable organisation names as keywords (e.g. all:DeepMind OR all:Anthropic) combined with subject category filters (e.g. cs.AI, cs.LG). This means any paper on arXiv that mentions a configured organisation in its title, abstract, or author affiliations is eligible for ingestion.

To pull in papers from a specific lab, add its name to ARXIV_ORGS in your .env. To focus on a particular research area, adjust ARXIV_CATEGORIES. Common examples:

OrganisationAdd to ARXIV_ORGS
Google DeepMindDeepMind
AnthropicAnthropic
OpenAIOpenAI
Meta AIMeta AI
MistralMistral
Microsoft ResearchMicrosoft Research
Stanford HAIStanford
Berkeley AI ResearchBerkeley

How it works

arXiv papers
│
▼
Fetch Search arXiv for papers from configured orgs and categories
│
▼
Analyse LLM extracts key claims, methods, open questions per paper
│
▼
Gap map LLM identifies cross-paper research gaps and opportunities
│
▼
Synthesise LLM generates N startup ideas from the gap map
│
▼
Score LLM rates each idea on novelty (0–1) and feasibility (0–1)
│
▼
Select Top N ideas are persisted and tagged with the run ID
│
▼
Connect Ideas sharing source papers are linked to each other

Each pipeline run is saved independently so the History page accumulates every batch — you never lose earlier ideas when you run again.

Fast re-synthesis path: When no new arXiv papers are found, the pipeline skips the expensive per-paper LLM calls and builds lightweight pseudo-analyses directly from abstracts. Only two LLM calls are made (gap map + synthesise) instead of 30+, so subsequent runs on the same day complete in seconds rather than minutes.


Tech stack

LayerTechnology
Backend APIFastAPI + SQLAlchemy (async)
DatabaseSQLite (dev) / PostgreSQL (prod)
MigrationsAlembic
WorkerPython threads, APScheduler
LLM runnersClaude CLI, Codex CLI, Gemini CLI, Anthropic API, Gemini API, OpenRouter
FrontendReact 18 + TypeScript + Vite
Data fetchingTanStack React Query
StateZustand

Project structure

whitespace/
├── start.sh # One-command startup (installs deps, runs migrations, starts both servers)
├── backend/
│ ├── app/
│ │ ├── api/routes/ # FastAPI route handlers
│ │ │ ├── ideas.py # Today's feed, history, idea detail, surprise
│ │ │ ├── saved.py # Save / unsave ideas
│ │ │ ├── build.py # Trigger and fetch product sketch builds
│ │ │ ├── export.py # Export ideas as Markdown / PDF
│ │ │ └── system.py # Health, pipeline status/trigger, runner config, data sources
│ │ ├── db/
│ │ │ ├── models/ # SQLAlchemy ORM models
│ │ │ └── migrations/ # Alembic migration versions
│ │ ├── runners/ # LLM runner adapters (see Runner section)
│ │ ├── pipeline/ # Analysis, gap mapping, chunking, scoring utilities
│ │ └── core/config.py # Pydantic settings (reads .env)
│ └── worker/
│ ├── orchestrator.py # Full pipeline orchestration logic
│ ├── stages/ # fetch, analyse, gap_map, synthesise, score, select, connect
│ ├── prompts/ # Markdown prompt templates for each LLM stage
│ └── build_generator.py # Product sketch generation for saved ideas
└── frontend/
└── src/
├── pages/ # FeedPage, HistoryPage, IdeaDetailPage, SavedPage, SettingsPage, BuildOutputPage
├── components/ # NavBar, IdeaCard, HeroCard, BadgeRow, ScoreBar, ConnectedIdeas
├── hooks/ # useIdeas, useSaved, useBuild (React Query hooks)
└── api/ # Typed API client

Quick start

git clone https://github.com/dgtise25/whitespace.git
cd whitespace
bash start.sh

That single command:

  1. Stops any existing servers on ports 18730 / 18731
  2. Creates backend/.env with SQLite defaults if it doesn't exist
  3. Creates and activates a Python virtualenv, installs all dependencies
  4. Installs frontend npm packages
  5. Runs Alembic migrations
  6. Starts the FastAPI backend on http://localhost:18730
  7. Starts the Vite frontend on http://localhost:18731

Open http://localhost:18731 in your browser, then click Refresh Ideas to run the pipeline.


Docker

./start.sh --docker

Builds and starts Postgres, the FastAPI backend, and the Vite frontend via docker compose:

ServiceContainer portHost port
Postgres543218733
Backend API800018730
Frontend517318731

Config is read from the repo-root .env (see Configuration). Source lives under backend/ and frontend/ and is bind-mounted into the containers, so edits hot-reload without a rebuild.

The scheduled worker (daily pipeline run) is opt-in — it's defined behind a compose profile so it doesn't start by default:

docker compose -f docker/docker-compose.dev-minimal.yml --profile worker up

To run the compose stack directly instead of via start.sh:

docker compose -f docker/docker-compose.dev-minimal.yml up --build

Configuration

Copy backend/.env.example to backend/.env and edit as needed:

# Database — SQLite for local dev, switch to postgres:// for productionDATABASE_URL=sqlite+aiosqlite:///./whitespace.db# LLM runner — configure at least one (see Runner section below)# ANTHROPIC_API_KEY=sk-ant-...# GEMINI_API_KEY=AIza...OPENROUTER_API_KEY=sk-or-...# OPENROUTER_ANALYSIS_MODEL=anthropic/claude-opus-5 # optional — pins an exact slug# Leave unset and Whitespace auto-resolves the newest Claude Opus model OpenRouter offers# Pipeline mode: "full" uses a real LLM; "stub" inserts fixture data (fast, no API calls)PIPELINE_MODE=full# Scheduled daily run time (24-hour clock, UTC)WORKER_SCHEDULE_HOUR=2WORKER_SCHEDULE_MINUTE=0# arXiv organisations to source papers fromARXIV_ORGS=DeepMind,Anthropic,OpenAI# arXiv subject categories to includeARXIV_CATEGORIES=cs.AI,cs.LG,cs.CL,cs.MA# Number of ideas to generate per pipeline runIDEAS_PER_RUN=8

arXiv categories reference

CodeSubject
cs.AIArtificial Intelligence
cs.LGMachine Learning
cs.CLComputation and Language (NLP)
cs.MAMulti-Agent Systems
cs.SESoftware Engineering
cs.HCHuman-Computer Interaction
eess.SPSignal Processing

LLM runners

Whitespace picks the first available runner in this priority order:

PriorityRunnerHow to enable
1Claude CLIInstall the Claude Code CLI — no API key required
2Codex CLIInstall the OpenAI Codex CLI
3Gemini CLIInstall the Gemini CLI
4Gemini APISet GEMINI_API_KEY
5Anthropic APISet ANTHROPIC_API_KEY
6OpenRouterSet OPENROUTER_API_KEY

You can override the active runner at runtime from the Settings page in the UI, or via the API:

# See which runners are available and which is active
curl http://localhost:18730/api/system/runners
# Pin to a specific runner
curl -X PUT http://localhost:18730/api/system/runner \
-H "Content-Type: application/json" \
-d '{"name": "anthropic"}'

API reference

All endpoints are prefixed with /api. Interactive docs at http://localhost:18730/docs.

Ideas

MethodPathDescription
GET/api/ideas/todayToday's featured ideas (falls back to most recent run if none today)
GET/api/ideas/historyAll ideas grouped by pipeline run, newest first
GET/api/ideas/surpriseRandom featured idea
GET/api/ideas/{id}Full idea detail including connected ideas

Example — fetch today's feed:

curl http://localhost:18730/api/ideas/today
{
"date": "2026-04-25",
"papers_ingested": 12,
"ideas": [
{
"id": "3fa85f64-...",
"title": "Federated Gap Detector",
"description": "A privacy-preserving system that surfaces research blind spots across siloed lab corpora without exposing raw data.",
"badge": "Novel",
"novelty_score": 0.91,
"feasibility_score": 0.74,
"is_featured": true,
"featured_date": "2026-04-25"
}
]
}

Example — fetch idea detail:

curl http://localhost:18730/api/ideas/3fa85f64-...
{
"id": "3fa85f64-...",
"title": "Federated Gap Detector",
"description": "...",
"why_novel": "No existing tool combines federated learning with cross-corpus gap analysis.",
"who_builds": "ML infrastructure teams at research-heavy organisations.",
"who_buys": "AI labs, pharma companies, government research bodies.",
"novelty_score": 0.91,
"feasibility_score": 0.74,
"badge": "Novel",
"paper_ids": ["2404.12345", "2404.67890"],
"connections": [
{
"id": "abc-...",
"title": "Cross-Silo Knowledge Distillation",
"badge": "Feasible",
"shared_paper_count": 2
}
]
}

Saved ideas

MethodPathDescription
GET/api/saved/List all saved ideas
POST/api/saved/Save an idea {"idea_id": "..."}
DELETE/api/saved/{idea_id}Remove a saved idea

Example:

# Save an idea
curl -X POST http://localhost:18730/api/saved/ \
-H "Content-Type: application/json" \
-d '{"idea_id": "3fa85f64-..."}'# List saved ideas
curl http://localhost:18730/api/saved/

Build output (product sketch)

MethodPathDescription
GET/api/build/{idea_id}Fetch existing product sketch
POST/api/build/{idea_id}Trigger product sketch generation (async, returns 202)

Example:

# Trigger a build
curl -X POST http://localhost:18730/api/build/3fa85f64-...
# Poll until ready (status changes from "generating" to "ready")
curl http://localhost:18730/api/build/3fa85f64-...
{
"idea_id": "3fa85f64-...",
"status": "ready",
"product_sketch": {
"tagline": "Find the gaps your competitors can't see.",
"target_user": "Research leads at AI labs",
"core_loop": "Ingest → Analyse → Surface → Act",
"risks": [
{ "title": "Data access", "description": "Labs may not share paper corpora." }
],
"monetisation": [
{ "name": "SaaS subscription", "fit": "high", "description": "Per-seat pricing for research teams." }
]
}
}

Export

MethodPathDescription
GET/api/export/{idea_id}/markdownDownload idea + build as a .md file
GET/api/export/{idea_id}/pdfDownload idea + build as a .pdf file
curl http://localhost:18730/api/export/3fa85f64-.../markdown -o idea.md
curl http://localhost:18730/api/export/3fa85f64-.../pdf -o idea.pdf

System

MethodPathDescription
GET/api/system/healthHealth check — API and database status
GET/api/system/pipeline/statusWhether pipeline is running + last completed run
POST/api/system/pipeline/runTrigger a pipeline run manually
GET/api/system/runnersList available LLM runners and active runner
PUT/api/system/runnerSet preferred runner
GET/api/system/configCurrent data source configuration
PUT/api/system/data-sourcesUpdate active orgs and categories

Example — trigger the pipeline:

curl -X POST http://localhost:18730/api/system/pipeline/run
{ "status": "started", "message": "Pipeline started in background." }

Example — update data sources:

curl -X PUT http://localhost:18730/api/system/data-sources \
-H "Content-Type: application/json" \
-d '{"orgs": ["DeepMind", "Anthropic"], "categories": ["cs.AI", "cs.CL"]}'

Frontend pages

PageRouteDescription
Ideas/Today's featured ideas — hero card for the top idea, grid below
History/historyEvery pipeline run accumulated over time, filterable by badge
Idea detail/ideas/:idFull breakdown: why novel, who builds, who buys, connected ideas
Build/ideas/:id/buildAI-generated product sketch for a specific idea
Saved/savedIdeas you've bookmarked
Settings/settingsConfigure LLM runner, arXiv orgs, and categories

The NavBar polls the pipeline status every 8 seconds. When a run completes, the Ideas and History pages refresh automatically — no manual reload needed.


Database models

TablePurpose
papersRaw arXiv papers (title, abstract, authors, categories, URL)
chunksText chunks derived from paper abstracts
ingestion_runsOne row per pipeline run — tracks papers fetched, ideas generated, errors
ideasGenerated ideas with scores, badges, and run_id linking to the ingestion run
connected_ideasPairs of ideas that share source papers, ranked by shared paper count
saved_ideasUser bookmarks linking to ideas
build_outputsAI-generated product sketches for saved ideas

Badges

Each idea receives one badge based on its scores:

BadgeMeaning
NovelHigh novelty (≥ 0.7), lower feasibility — forward-looking research opportunity
FeasibleHigh feasibility (≥ 0.7), lower novelty — buildable with current technology
EmergingBoth scores moderate — interesting but early
SpeculativeBoth scores lower — long-horizon, high-risk

Running in production

Switch to PostgreSQL by updating DATABASE_URL:

DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/whitespace

Run migrations with the synchronous driver:

DATABASE_URL=postgresql+psycopg://user:password@localhost:5432/whitespace \
alembic upgrade head

Start the backend with a production ASGI server:

uvicorn app.main:app --host 0.0.0.0 --port 18730 --workers 2

Build the frontend for production:

cd frontend && npm run build
# Serve the dist/ folder with nginx or any static host

Development

# Backend testscd backend && pytest
# Frontend testscd frontend && npm test# Type-check frontendcd frontend && npm run build
# Lint backendcd backend && ruff check .

To use the stub pipeline (no LLM calls, instant fixture data):

PIPELINE_MODE=stub

About

Research synthesis engine — surfaces novel ideas from arXiv, GitHub, blogs, and academic sources

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages