import { apiPost } from "@/lib/api/client"; /** * Learn Anything playable lesson. The backend generates this once per unique * lesson (LLM teaching script -> Deepgram/AI4Bharat audio) and caches it by * content hash, so repeat opens are free and instant. The browser "plays" it * — audio + synced board — instead of streaming a rendered MP4. */ export interface LessonBeat { kind: string; narration: string; board_heading: string; board_lines: string[]; visual_hint: string; audio_src: string; start_second: number; duration_seconds: number; } export interface LessonManifest { schema: string; lessonHash: string; topic: string; lessonTitle: string; level: string; medium: string; voice: string; /** Audio when available; reading is an explicit, fully navigable fallback. */ deliveryMode?: "audio" | "reading"; /** Honest explanation when the class opens without generated voice. */ deliveryNotice?: string; isFallback?: boolean; totalSeconds: number; totalMinutes: number; /** One-line description of the class (added v1.1; older cached lessons omit it). */ summary?: string; /** "By the end you can…" outcomes (added v1.1; older cached lessons omit it). */ objectives?: string[]; beats: LessonBeat[]; notes: string[]; flashcards: { front: string; back: string }[]; } export interface LearnLessonRequestBody { topic: string; lesson_title: string; level?: string; medium?: string; source_ids?: string[]; } /** * Build (first time) or fetch (cached) a playable lesson. First call for a new * lesson takes a while — the server is writing the script and voicing it — so * callers should show a "preparing your class" state. */ export function fetchLearnLesson(body: LearnLessonRequestBody): Promise { // The backend returns a voiced lesson when explicitly enabled, otherwise // it returns the existing labelled reading manifest within its SLA. Keep a // browser-side ceiling as the final protection against a stale deployment // or a provider that ignores cancellation. return apiPost( "/generate/learn-lesson", body, { timeoutMs: 45_000 }, ); }