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

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, orchestration in lib/workbench/use-workbench-session.ts, and media handling in 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 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 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 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, provides generation fencing that isolates consecutive lesson builds from one another.

// 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 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 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).

// 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
Session ID fencing Isolates different generations of the same stage lib/utils/chat-storage-core.ts
Placeholder status Tracks which media assets exist vs. pending lib/store/media-generation.ts
Completion flags Enables skip/resume logic on reload lib/store/stage.ts, lib/utils/database.ts

On page load, 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.


Summary


Frequently Asked Questions

What files control the lesson generation pipeline in OpenMAIC?

The pipeline spans lib/types/generation.ts (type definitions), lib/workbench/use-workbench-session.ts (orchestration), lib/server/agent-runtime/generation-tools.ts (LLM tools), lib/store/media-generation.ts (placeholder tracking), and 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 and a runtimeGenerationSessionId in 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) and stage records store generationComplete flags (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. Once placeholders exist, useMediaGenerationStore detects them and launches the appropriate generation tasks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →