File size: 2,215 Bytes
65d68f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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<LessonManifest> {
  // 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<LessonManifest, LearnLessonRequestBody>(
    "/generate/learn-lesson",
    body,
    { timeoutMs: 45_000 },
  );
}