From trending cancer paper to reusable data in minutes. NCI Digest closes the gap between discovering a paper and reusing what it shared.
Live prototype: https://nci-digest.k8s.maayanlab.cloud/ Flagship collection: NCI Signal, a weekly AI podcast on the NCI-supported paper that drew the most PubMed attention.
- Shared data is hard to find from the paper. NCI-funded papers deposit data, code, and tools in GEO, GDC, GitHub, and other repositories, but the accessions are buried in availability statements and supplements, and most readers never follow them.
- Busy researchers and clinicians don't have time to read every paper. A short, accurate explainer they can listen to or skim gets the work in front of people who would otherwise skip it.
- Each explainer ends with the reusable outputs. Every item links straight to the datasets, code, and tools the paper shared, so learning about the work leads directly to reusing it.
- Overview & Core Features
- System Architecture & Pipeline
- Formats & Content Types
- Tech Stack
- Quick Start (Local Development)
- Using the Platform
- Alignment with FAIR Principles & NCI ODS Goals
- License
- Roadmap
The project has two layers:
| What it is | |
|---|---|
| NCI Signal | The flagship, curated weekly collection maintained by the Ma'ayan Lab: the most-discussed NCI-funded paper each week, reviewed before publication. |
| NCI Digest | The community platform. Signed-in users create and share their own collections, defined by topic, date window, or a raw PubMed query. |
Core features:
- Automated PubMed Trending ingestion. Searches PubMed for papers with NCI grant support in a rolling date window, intersects them with the PubMed Trending list (~1,000 PMIDs), and ranks by trending position. OpenAlex adds citation counts and open-access PDF locations, with PubMed Central as a fallback. Users can also upload their own PDF.
- Multi-repository output mining (20+ repositories). Scans the title, abstract, full-text PDF (including the data-availability pages at the end), and Europe PMC data links for accessions and URLs across GEO, SRA, BioProject, dbGaP, ArrayExpress, PRIDE, GDC, IDC, PDC, CRDC, GitHub, GitLab, Hugging Face, Zenodo, Figshare, Dryad, OSF, protocols.io, Bioconductor, PDB, BioStudies, and MetaboLights.
- Strictly source-grounded generation. Every prompt is wrapped in fixed source rules: the paper (PDF or abstract) is the only factual source, and the model may not invent findings, numbers, quotations, datasets, or accessions. Creator instructions cannot override these rules. When only an abstract is available, the item is shorter and says so on the page.
- Four explainer formats. Podcast, TED-style talk, slide deck with speaker notes, and narrated video (slides played in sync with an AI voice-over).
- Custom user collections. Users choose the format, model, a structure prompt, keywords, an optional PubMed query, a date window (7/14/30/90 days), and filters for NCI-only, flagship journals, or papers that share data or code.
- Human-in-the-loop review. Flagship NCI Signal episodes are saved as unpublished drafts and must be edited and approved in the Admin dashboard before release. Collection owners can hide or publish any item they generate.
- Shareable by default. Each item page has X, LinkedIn, and copy-link sharing with server-rendered link previews.
┌───────────────────────────────┐
│ 1. PAPER INGESTION │ PubMed E-utilities (NCI grant filter, date window)
│ PubMed / OpenAlex │ ∩ PubMed Trending → ranked candidates
│ │ + OpenAlex / PMC open-access PDFs, or user PDF upload
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ 2. OUTPUT EXTRACTION │ Regex scan of title, abstract, PDF text
│ 20+ repos (GEO/GDC/GitHub) │ + Europe PMC data links → deduplicated, ranked outputs
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ 3. GROUNDED LLM PROMPTING │ Source rules + format spec + creator structure prompt
│ script / slides / notes │ → strict JSON contract → TTS audio (podcast, talk, video)
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ 4. DRAFT REVIEW │ Flagship: unpublished draft → Admin edit & approve
│ human in the loop │ Community: owner can hide or publish each item
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ 5. NCI DIGEST PLATFORM │ Next.js gallery, collection & item pages,
│ publish & share │ "Reuse this work" links to every extracted output
└───────────────────────────────┘
Runtime: Next.js app ──enqueue──▶ PostgreSQL (Graphile Worker queue) ──▶ Node worker ──▶ Python tasks
Audio → S3-compatible storage · Slides, metadata, users → PostgreSQL
-
Paper ingestion (
src/tasks/paperFinder.py,src/tasks/signalPapers.py) Builds a PubMed query from the collection's criteria (NCI grant terms, keywords, optional raw query, date window, journal filter), then scrapes the PubMed Trending page because it has no JSON API. Candidates are ranked by trending position, with a bonus for papers that share outputs. OpenAlex supplies the open-access PDF URL; otherwise the PMC PDF is tried. -
Output extraction (
src/tasks/reusableOutputs.py) Pattern-matches accession formats (GSE…,phs…,PXD…,E-MTAB-…,PRJNA…) and repository URLs (github.com/…,portal.gdc.cancer.gov/…,imaging.datacommons.cancer.gov/…, Zenodo/Figshare/Dryad DOIs). It reads the first and last pages of the PDF, where data-availability statements usually are, and merges in Europe PMC data links. Results are normalized to HTTPS, deduplicated, ranked by repository type, and capped at 24 per paper. -
Grounded LLM prompting (
src/tasks/signalGenerator.py,src/tasks/scriptPrompts.py) The prompt is assembled from three fixed parts: source rules, a per-format specification, and a JSON response contract. The creator's structure prompt sits inside these and cannot override them. Extracted PDF text is preferred over the abstract. If generation from the full text times out, it retries from the abstract and marks the item as abstract-based. Spoken formats are voiced with TTS (src/tasks/audioGenerator.py). -
Draft review (
src/views/Admin.jsx,src/tasks/generatePodcast.py) Flagship episodes are added to the catalog aspublished: false. An admin edits the title, description, and script, then publishes. Community items can be hidden or shown by their owner throughPATCH /api/signals/:slug/items/:id. -
Platform (
src/app,src/views) Next.js App Router pages and API routes serve the community gallery (/), the flagship collection (/nci-signal), user collections, and item pages. Each item page ends with the extracted reusable outputs.
| Format | What you get | Ideal use case |
|---|---|---|
| 🎧 Podcast | Two AI hosts (Trinity and Axiom) discuss the paper: question, approach, key findings, limits, and what can be reused. | Commutes, workouts, staying current without screen time |
| 🎤 TED-style talk | One narrator, one big idea, told as a story arc that ends with a single takeaway. | Clinical breaks, quick context before a tumor board or seminar |
| 📊 Slides | A short deck (problem, approach, results, limits, shared outputs, takeaway) with speaker notes. | Lab meetings, journal clubs, teaching |
| 🎬 Narrated video | Slides played in sync with an AI voice-over, one scene per idea. | Onboarding trainees, outreach, social sharing |
Every format ends by pointing to the paper's data, code, and tools.
| Layer | Technology |
|---|---|
| Web app & API | Next.js 16 (App Router), React 18, Tailwind CSS, Radix UI |
| Database | PostgreSQL 17 via Kysely (type-safe queries and migrations) |
| Job queue | Graphile Worker, a PostgreSQL-backed queue |
| Pipeline | Python 3 (PubMed, OpenAlex, Europe PMC, pypdf, OpenAI SDK, Gemini REST API) |
| Storage | S3-compatible object storage, accessed with rclone |
| Auth | Google OAuth with signed session cookies; email-only dev login for local testing |
| Deployment | Docker (Dockerfile.app, Dockerfile.worker) and Docker Compose, deployed to the Ma'ayan Lab Kubernetes cluster |
| External APIs | PubMed E-utilities, PubMed Trending, OpenAlex, Europe PMC, iCite |
Models. Generation is provider-agnostic. The same source rules and JSON contract are sent to whichever model a collection uses:
| Provider | Models | Used for | Key |
|---|---|---|---|
| OpenAI | gpt-5.5-2026-04-23 (default), plus anything in SIGNAL_MODELS |
Scripts, slides, and all text-to-speech (gpt-4o-mini-tts) |
OPENAI_API_KEY |
| Google Gemini | gemini-2.5-flash (override with GEMINI_MODEL) |
Scripts and slides, from full-text PDFs or abstracts | GEMINI_API_KEY |
When GEMINI_API_KEY is set, the Gemini model appears in the collection editor's Model picker, so each collection owner can switch between providers. Set SIGNAL_DEFAULT_MODEL=gemini-2.5-flash to make Gemini the default for new collections. Any model whose name starts with gemini is sent to the Gemini API, and every other model goes to OpenAI. With the Gemini free tier, the Slides format runs at no cost. Spoken formats still use OpenAI for text-to-speech.
- Docker with Docker Compose
- Node.js 20 or newer, with npm
- Python 3.10 or newer
rclone(only needed to upload audio to S3)- An
OPENAI_API_KEY, aGEMINI_API_KEY, or both
git clone https://github.com/danielzhu04/NCI-podcast.git
cd NCI-podcast
cp .env.example .envEdit .env and set at least:
POSTGRES_PASSWORD=choose-a-password
DATABASE_URL=postgres://postgres:choose-a-password@localhost:5432/postgres
OPENAI_API_KEY=sk-... # scripts with OpenAI models, and all audio
GEMINI_API_KEY=... # optional: adds gemini-2.5-flash to the model picker
SESSION_SECRET=a-long-random-string
ADMIN_PASSWORD=pick-a-local-password
ALLOW_DEV_LOGIN=true # email-only sign-in, no Google OAuth needed locallyZero-cost setup: get a free key from Google AI Studio, set GEMINI_API_KEY and SIGNAL_DEFAULT_MODEL=gemini-2.5-flash, and create a Slides collection. Script and deck generation then runs on the Gemini free tier, with no OpenAI key needed.
docker compose up -d nci-signal-postgresnpm install
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
npx kysely migrate:latestnpm run dev # Next.js on http://localhost:3000In a second terminal:
source .venv/bin/activate
npm run worker # Graphile Worker: runs generation jobsGeneration runs in the worker, so both processes must be running.
docker-compose.yaml defines three services: nci-signal-app, nci-signal-worker, and nci-signal-postgres. To build and run everything in containers:
docker compose up -d --buildThe public hostname is PROD_NEXT_PUBLIC_URL. Compose maps that onto the cluster ingress (maayanlab.cloud/ingress) and APP_URL. Keep the Kubernetes app name nci-signal so this is an in-place update (same Postgres volume), not a second stack.
- In
.env, set:PROD_NEXT_PUBLIC_URL=https://nci-digest.k8s.maayanlab.cloud APP_URL=https://nci-digest.k8s.maayanlab.cloud
- In Google Cloud Console → OAuth client, add
https://nci-digest.k8s.maayanlab.cloud/api/auth/google/callback
as an authorized redirect URI (and the matching origin). You can remove the oldnci-signalURI after cutover. - Rebuild and apply the same way you last deployed this compose file to
danielzhu04@sshkube(namespacedanielzhu04). The ingress annotation will move the site to the new host;*.k8s.maayanlab.cloudTLS is issued automatically. - Confirm https://nci-digest.k8s.maayanlab.cloud/. After the ingress updates, https://nci-signal.k8s.maayanlab.cloud/ should stop serving this app. Do not rename the compose
x-kubernetes.nameunless you also delete the oldnci-signalresources — renaming creates a second deployment and leaves the old host up.
Flagship pages stay at /nci-signal on the new domain (that path is the collection slug, not the hostname). S3 (S3_PREFIX=nci-signal) can stay as-is; it is object storage, not the public site URL.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string |
OPENAI_API_KEY |
For OpenAI models and audio | Script generation with OpenAI models, and all TTS |
GEMINI_API_KEY |
Optional | Adds the Gemini model to the picker and enables Gemini generation |
GEMINI_MODEL |
Optional | Gemini model to offer (default gemini-2.5-flash) |
SIGNAL_DEFAULT_MODEL |
Optional | Default model for new collections (default gpt-5.5-2026-04-23) |
SESSION_SECRET |
✅ | Signs user session cookies |
ADMIN_PASSWORD |
✅ | Admin dashboard login at /admin |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
Production | Google sign-in. Register <origin>/api/auth/google/callback as the redirect URI. |
ALLOW_DEV_LOGIN |
Local only | Email-only sign-in. Ignored in production builds. |
SIGNAL_MODELS |
Optional | Extra selectable models, comma-separated |
SIGNAL_DAILY_LIMIT |
Optional | Generations per user per 24 hours (default 5) |
NCI_SCRIPT_PROMPT |
Optional | Flagship prompt set: v1_paper_discussion or v2_ted_talk |
RCLONE_CONFIG_MAAYANLAB_*, S3_* |
For audio | S3-compatible storage for generated audio |
- Browse.
/is the community gallery, with NCI Signal pinned first at/nci-signal. - Create a collection. Sign in, go to
/signals/new, and choose a format, model, structure prompt, paper criteria, and visibility (public or unlisted). - Generate. On the collection page, search matching papers (ranked by PubMed Trending) or upload a PDF, then generate. Audio is stored in S3 under
nci-signal/signals/<slug>/, and slides are stored in PostgreSQL. - Review the flagship.
/adminlists flagship drafts to edit and publish. - Scoped feeds. Save a PubMed topic as a candidate feed from the Admin page or the command line:
npm run feed -- list
npm run feed -- add --name "Pancreatic cancer" \
--topic 'pancreatic neoplasms[mh] OR "pancreatic cancer"[tiab]'
npm run feed -- run pancreatic-cancer --limit 10Add --no-nci to drop the NCI-grant requirement for a feed.
NCI's data-sharing policy has made cancer research outputs Findable and Accessible: they are deposited in GDC, IDC, PDC, GEO, dbGaP, and GitHub. Reuse still depends on a researcher noticing the accession, often buried in a data-availability statement. NCI Digest puts those deposits in front of the reader at the moment they are learning about the paper, so a dataset that is findable in principle becomes one they actually open.
| FAIR principle | How NCI Digest supports it |
|---|---|
| Findable | Accessions are extracted from full text and Europe PMC data links and shown on every item, so outputs are discoverable from the paper itself. |
| Accessible | Each output links directly to its repository landing page (GDC, IDC, GEO, dbGaP, GitHub, and others) over HTTPS. |
| Interoperable | Outputs are normalized to a common {type, id, url} record across 20+ repositories, ready for downstream indexing or cross-linking. |
| Reusable | Each explainer covers methods and limitations and ends with "what you could reuse," giving readers the context needed to judge whether the data fit their question. |
This supports the NCI Office of Data Sharing (ODS) goal of measurable secondary use of NCI-funded data. It makes outputs from CRDC resources (GDC, IDC, PDC) visible to clinicians, trainees, and researchers in neighboring fields who might never search those portals directly.
This project is intended to be released under the MIT License. A LICENSE file will be added to the repository root. Generated explainers summarize published research. Rights to the source papers and deposited datasets stay with their authors and repositories.
- Fully zero-cost audio: add Gemini text-to-speech so podcast, talk, and video formats also run on the free tier.
- Gemini for the flagship: route the weekly NCI Signal episode (currently OpenAI only) through the same model switch.
- Altmetric API integration: add Altmetric attention scores to PubMed Trending when ranking papers. A connector already exists but is currently disabled.
- Section 508 accessibility: auto-generated captions and transcripts for audio and video, and accessible slide exports.
- CRDC webinar cross-linking: link extracted GDC, IDC, and PDC outputs to related CRDC webinars, tutorials, and workspace notebooks.
- Reuse analytics: track click-throughs from explainers to repositories as a measure of data-sharing impact.
Built by the Ma'ayan Lab at the Icahn School of Medicine at Mount Sinai. Submitted as supplementary evidence for the NCI Data Sharing Impact Prize, Track 1: Ideas.