Architected and shipped an end-to-end meeting summarisation system for ChatSDK. Ingests raw video/audio recordings from Whereby, executes multi-provider LLM summarisation passes with async background polling, and visualises participant insights and structured action items in a Next.js dashboard.
Listens for Whereby room session completion events, extracts audio streams, and dispatches them to background transcription workers.
Chunks transcripts and routes requests across LLM providers with automatic fallback, generating executive takeaways, topic clusters, and categorized action items.
Engineered fault-tolerant job polling to handle variable transcription latency, caching intermediate stages to prevent duplicate LLM calls during network reconnections.
Relational PostgreSQL schema managed via Drizzle ORM. Built the Meeting Summary Dashboard UI in Next.js using shadcn/ui, Tailwind CSS, and Framer Motion.
import { pgTable, text, timestamp, uuid, jsonb, integer } from "drizzle-orm/pg-core";
// Meeting Session Record
export const meetingSessions = pgTable("meeting_sessions", {
id: uuid("id").defaultRandom().primaryKey(),
wherebyRoomId: text("whereby_room_id").notNull(),
recordingUrl: text("recording_url"),
durationSeconds: integer("duration_seconds"),
status: text("status", { enum: ["queued", "processing", "completed", "failed"] }).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Structured Meeting Insights & Action Items
export const meetingSummaries = pgTable("meeting_summaries", {
id: uuid("id").defaultRandom().primaryKey(),
sessionId: uuid("session_id").references(() => meetingSessions.id, { onDelete: "cascade" }),
executiveSummary: text("executive_summary").notNull(),
actionItems: jsonb("action_items").$type<Array<{ assignee: string; task: string; priority: "low" | "medium" | "high" }>>(),
participantMetadata: jsonb("participant_metadata").$type<Array<{ name: string; talkTimePercent: number }>>(),
tokensUsed: integer("tokens_used"),
generatedAt: timestamp("generated_at").defaultNow().notNull(),
});