# How the Two-Stage Lesson Generation Pipeline Works in OpenMAIC's lib/generation

> Explore OpenMAIC's two-stage lesson generation pipeline. Discover how it creates structured outlines with LLMs and then generates slide decks, images, audio, and interactive media.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-02

---

**OpenMAIC builds lesson content through a two-stage process that first generates a structured outline with an LLM, then produces slide decks, images, audio, and interactive media from that outline.**

The two-stage lesson generation pipeline in OpenMAIC cleanly separates *architectural planning* from *asset creation*. This design prevents race conditions, enables progress tracking, and allows generation jobs to resume after page refreshes. The pipeline lives across multiple modules in the codebase, with core logic in [`lib/types/generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts), orchestration in [`lib/workbench/use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/use-workbench-session.ts), and media handling in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) and related files.

---

## Stage 1: Outline Generation

The first stage uses a large language model to create a structured lesson blueprint. This includes chapters, sections, learning objectives, and the high-level content flow.

**Key components driving outline generation:**

- **[`lib/types/generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts)** defines the `LessonOutline` type and related generation interfaces. These type definitions enforce the contract between the LLM output and downstream consumers.

- **[`lib/workbench/use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/use-workbench-session.ts)** manages the generation lifecycle. It maintains a `generation` ref that increments with each new request, ensuring only results from the current generation are applied. The hook dispatches a `START_GENERATION` action and stores the resulting outline in the stage's generation store.

- **[`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts)** contains server-side definitions for the "generation tools" that invoke the LLM. The agent runtime calls these tools, receives a JSON outline, and persists it to the stage record.

The outline is stored with `generationComplete: false` until the second stage finishes. This flag, combined with a `runtimeGenerationSessionId` in [`lib/utils/chat-storage-core.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/utils/chat-storage-core.ts), provides *generation fencing* that isolates consecutive lesson builds from one another.

```typescript
// lib/workbench/use-workbench-session.ts (conceptual)
function startLessonGeneration() {
  // Increment generation token to fence this request
  generation.current += 1;
  
  dispatch({
    type: 'START_GENERATION',
    token: generation.current,
    stageId: activeStage.id
  });
  
  // Server-side agent runtime invokes generation-tools.ts
  // Result: JSON outline written to stage.outline
}

```

---

## Stage 2: Media and Content Generation

Once the outline exists, the pipeline generates slide decks, images, audio, video, and other interactive assets. This stage is **placeholder-driven**, meaning the system creates references for required media before the actual content exists.

**How media generation works:**

- **[`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts)** implements a Dexie-backed store that tracks both placeholders and completed media tasks. Placeholders represent pending assets (slides, images, TTS segments) with unique IDs and status flags.

- **[`lib/workbench/media-lifecycle.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/media-lifecycle.ts)** monitors placeholder references via `useMediaGenerationStore`. When it detects new placeholders, it launches appropriate generation jobs—image synthesis, text-to-speech, video stitching—and updates the store on completion.

- **`lib/video-export-app/*`** compiles finished assets into exportable packages (video files, HTML presentations).

```typescript
// Placeholder-driven media pipeline
useEffect(() => {
  const placeholders = useMediaGenerationStore.getState().placeholders;
  
  placeholders
    .filter(ph => ph.status === 'pending')
    .forEach(ph => {
      // Launch generation based on placeholder type
      switch (ph.type) {
        case 'slide':
          generateSlideImage(ph);
          break;
        case 'narration':
          generateTTS(ph);
          break;
        case 'video-segment':
          generateVideoClip(ph);
          break;
      }
    });
}, [placeholders]);

```

When all placeholders resolve, the stage record's `generationComplete` flag flips to `true`. This triggers UI updates and enables export functionality.

---

## State Management and Resumability

The pipeline persists critical state to support recovery across refreshes.

| Mechanism | Purpose | Location |
|-----------|---------|----------|
| **Generation token** | Prevents stale LLM responses from overwriting current work | [`lib/workbench/use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/use-workbench-session.ts) |
| **Session ID fencing** | Isolates different generations of the same stage | [`lib/utils/chat-storage-core.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/utils/chat-storage-core.ts) |
| **Placeholder status** | Tracks which media assets exist vs. pending | [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) |
| **Completion flags** | Enables skip/resume logic on reload | [`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts), [`lib/utils/database.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/utils/database.ts) |

On page load, [`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts) checks the stage record's `generationComplete` flag and the outline's completion status. If generation finished, the UI skips the pipeline. If interrupted, the system resumes in-flight jobs by scanning for `pending` placeholders in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts).

---

## Summary

- **Stage 1 (Outline)** uses [`lib/types/generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts), [`lib/workbench/use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/use-workbench-session.ts), and [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts) to generate and store a structured lesson blueprint.
- **Stage 2 (Media)** leverages [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) and [`lib/workbench/media-lifecycle.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/media-lifecycle.ts) to produce assets from placeholder definitions, with final compilation in `lib/video-export-app/*`.
- **Generation fencing** via tokens and session IDs in [`lib/utils/chat-storage-core.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/utils/chat-storage-core.ts) prevents race conditions between concurrent generation requests.
- **Resumable design** through Dexie persistence in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) and [`lib/utils/database.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/utils/database.ts) allows generation to continue after browser refreshes.

---

## Frequently Asked Questions

### What files control the lesson generation pipeline in OpenMAIC?

The pipeline spans [`lib/types/generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts) (type definitions), [`lib/workbench/use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/use-workbench-session.ts) (orchestration), [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts) (LLM tools), [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) (placeholder tracking), and [`lib/workbench/media-lifecycle.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/media-lifecycle.ts) (job launching).

### How does OpenMAIC prevent race conditions during generation?

The system uses a **generation token** incremented in [`use-workbench-session.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/use-workbench-session.ts) and a `runtimeGenerationSessionId` in [`chat-storage-core.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/chat-storage-core.ts) to fence each generation request. Only responses matching the current token are applied, discarding stale results from prior requests.

### Can generation resume after a browser refresh?

Yes. Placeholder states persist in Dexie ([`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts)) and stage records store `generationComplete` flags ([`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts)). On reload, the system checks these flags and resumes any pending media generation jobs automatically.

### What triggers the transition from outline to media generation?

The outline's presence in the stage record with `generationComplete: false` triggers placeholder creation in [`media-lifecycle.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/media-lifecycle.ts). Once placeholders exist, `useMediaGenerationStore` detects them and launches the appropriate generation tasks.