Turn a live conversation or meeting recording into a fact-checked, cited brief — and an optional audio briefing.
Meetings and conversations are full of claims you can't verify in the moment. Live Brief listens (mic or uploaded file), pulls out the entities, claims and topics, researches each claim on the web, marks every one as supported / unverified / contradicted with source links, and synthesises a concise, cited brief — optionally narrated as a single-host or two-host audio briefing.
It's a small Next.js app with no database. Every third-party call runs in a serverless
route handler, and every API key is read server-side only (never NEXT_PUBLIC_*). It is
vendor-flexible — point the LLM calls at any OpenAI-compatible endpoint — and degrades
gracefully: with no keys at all, it runs end-to-end on a sample transcript and deterministic
heuristics, so the whole flow is demoable offline.
mic / file ─▶ transcribe ─▶ distil ─▶ research ─▶ audit ─▶ report ─▶ 🔊
- Transcribe — chunked, pseudo-streaming speech-to-text from the mic or an uploaded file
- Distil — clean the transcript and extract entities, claims and topics
- Research — advanced web search on the subject and its claims
- Audit — cross-check each claim against sources → supported / unverified / contradicted, with links
- Report — synthesise a concise, cited brief
- Audio briefing (optional) — a single-narrator or two-host podcast of the brief, via text-to-speech
Every stage streams to an animated workflow diagram over Server-Sent Events (/api/orchestrate).
- 🔎 Claim-level auditing — each claim cross-checked and labelled supported / unverified / contradicted, with source links
- 🎙️ Live or uploaded — chunked pseudo-streaming STT from the mic, or one-shot from a file
- 🗣️ Speaker diarization — text-level turns you can rename, feeding the pipeline and the audio briefing
- 🔊 Audio briefing — single-narrator or two-host podcast of the final brief
- 📊 Animated workflow — every stage streamed live over Server-Sent Events
- 🧩 Graceful degradation — runs fully offline on a sample transcript when no keys are set
- 🔑 BYOK — bring your own keys from the UI; stored only in your browser, never on the server
- 🔐 Keys never client-bundled — no database, no
NEXT_PUBLIC_*secrets; requests carry keys as headers - 🔌 Vendor-flexible LLM — any OpenAI-compatible endpoint via base URL + model
Live Brief is built for fact-checking a spoken conversation against the open web. Its report is shaped as an investment-screening memo — team · traction · market · risks · recommendation, plus an NPS-style conviction score — so the sharpest fit is VC diligence, but the same pipeline serves any conversation full of checkable claims:
- 🧑💼 VC / angel diligence — record a founder call and get a cited screening memo: which traction and market claims check out, which stay unverified, and a risk + conviction read. (This is the built-in report shape.)
- 🎙️ Journalists & researchers — interview a source, then audit their factual claims against the web before publishing.
- 👥 User & customer research — turn a call into a structured, sourced summary instead of re-listening to the recording.
- 🧭 Meetings & panels — fact-check a recorded discussion and hand out a cited brief afterwards.
- 🎧 On the go — generate a short audio briefing and listen back to the verdict while commuting.
Every key is optional and there are three ways to supply one — they're tried in this order:
- In the UI — click 🔑 Keys in the capture bar, paste your keys, hit Save. They're
stored only in your browser (
localStorage) and sent with each request asx-*headers. The server reads them per-request and never persists them. - Server env — set them in
.env.local(see Environment) as the fallback for any field a user leaves blank. Good for a shared/self-hosted instance. - Nothing — the app still runs end-to-end on a sample transcript and deterministic heuristics.
Each pipeline stage degrades gracefully when its key is absent — using a heuristic fallback or being skipped with a note.
The default column is what ships; the swap for column lists drop-in alternatives.
| Capability | Used for | Default | Swap for |
|---|---|---|---|
| LLM (OpenAI-compatible Chat Completions) | distil · audit · report · diarize · podcast script | OpenAI gpt-4o-mini |
Drop-in via base URL + model — Groq · Together · OpenRouter · DeepSeek · Mistral · xAI · local Ollama / vLLM / LM Studio |
| Web research | research + claim audit | Tavily | Brave Search · Serper · Exa · SerpAPI · Bing — needs a small adapter in tavilySearch (different response shape) |
| Speech (STT + TTS) | live/upload transcription · audio briefing | Gradium | STT: Whisper API · Deepgram · AssemblyAI · Groq Whisper · local faster-whisper · TTS: OpenAI TTS · ElevenLabs · Cartesia · Azure · edge-tts (free) — needs an adapter (bespoke API shape) |
The LLM is fully swappable with zero code changes — point it at any OpenAI-compatible endpoint
by setting the base URL and model (in the UI or via OPENAI_BASE_URL / EXTRACT_MODEL). Web
research and speech use provider-specific request/response shapes, so swapping those means editing
the matching helper in lib/agents.ts (tavilySearch, listVoices / ttsPcm,
and the STT call in app/api/stt/route.ts).
Requires Node.js ≥ 18.
git clone https://github.com/weijt606/livebrief.git
cd livebrief
npm install
cp .env.example .env.local # fill in the keys you have (all optional)
npm run dev # http://localhost:3000Production:
npm run build
npm run startThe project builds to a standalone server (output: "standalone") and is straightforward to
containerise or deploy to any Node host (Vercel-ready).
Just want to see it? Run
npm run dev, open the app, and click Sample → Run — no keys required. The whole pipeline plays through on a built-in transcript.
Once the app is open at http://localhost:3000:
- (Optional) Add your keys. Click 🔑 Keys in the capture bar and paste any you have (OpenAI-compatible, Tavily, Gradium). Skip this to fall back to the server's env keys — or run the built-in sample with no keys at all. See BYOK.
- Get a transcript — three ways:
- Sample — loads a built-in founder-interview transcript. Zero setup; the best first run.
- Record — captures the mic and transcribes in near-real-time (needs a Gradium key, mic permission, and HTTPS or localhost).
- Upload — drop in an audio file for one-shot transcription.
- (Optional) Name the brief. Add a topic line — e.g. "Interview with Halcyon Grid — founder Dr. Lena Brandt" — so the pipeline anchors the company and people even if the name is never said aloud.
- (Optional) Identify speakers. Split the transcript into turns and rename
S1/S2to real people; the labelled transcript then feeds the pipeline and the audio briefing. - Run. Watch the animated workflow stream each stage live over SSE: distil → research → audit → report. Stages whose key is missing are skipped with a note rather than failing.
- Read the brief. A cited screening memo — summary, team, traction, market, risks, recommendation — with a risk/conviction gauge. Every claim is tagged supported / unverified / contradicted and linked to its source.
- (Optional) Generate the audio briefing. Pick single-narrator or two-host, choose the voice(s) and a target length, and synthesise a spoken version of the brief (needs a Gradium key).
See .env.example. All variables are optional and server-side only — they act as
the fallback for any key a user doesn't supply in the UI (see BYOK):
OPENAI_API_KEY— and optionallyEXTRACT_MODEL,OPENAI_BASE_URLTAVILY_API_KEYGRADIUM_API_KEY
Never commit real keys; .env.local is gitignored. For a public/shared deployment you can leave
these unset and let each visitor bring their own keys.
app/
api/orchestrate/route.ts # SSE orchestrator — runs the 5-stage pipeline
api/stt/route.ts # speech-to-text (chunked)
api/diarize/route.ts # speaker turns
api/extract|research/route.ts # standalone distil / web-search wrappers
api/voices, api/podcast/{script,audio}/route.ts # optional audio briefing
page.tsx # hosts the LiveBrief UI
lib/
agents.ts # server-only: chat (JSON), web search, distil/audit/report, TTS
orchestrate.ts # shared SSE types + helpers (client + server safe)
interview.ts # deterministic entity/claim heuristics + sample transcript
audio.ts # client-only: decode → 16 kHz mono WAV, recorder mime pick
components/
LiveBrief.tsx # the orchestrated workflow UI
ui/ # small presentational primitives
Browser MediaRecorder fragments aren't independently decodable, so the client keeps one
mic stream and cycles short (~2–8 s) recorder sessions; each finished clip is decoded and
resampled to 16 kHz mono WAV (lib/audio.ts) and posted to /api/stt, appending to a growing
transcript. Upload and the sample path are one-shot. (No browser-direct WebSocket: the STT
provider authenticates by API key, which must stay server-side.)
Next.js 15 (App Router) · React 19 · TypeScript · Tailwind CSS 3. No database.
Streaming via Server-Sent Events with X-Accel-Buffering: no so it streams unbuffered through nginx.
MIT — © 2026 weijt606.