How OpenMAIC Transforms Documents into Interactive Classroom Experiences: The 6-Step Pipeline Explained

OpenMAIC converts static documents into interactive classrooms through a modular pipeline that imports content, stores it as structured stage documents, generates AI-powered enhancements via LLMs, and renders them as fully interactive React-based lessons.

OpenMAIC is an open-source educational technology framework developed by THU-MAIC that bridges the gap between static teaching materials and dynamic, AI-enhanced learning environments. By combining document parsing, intelligent content generation, and real-time rendering, it enables educators to transform any PDF, PowerPoint, or text document into an engaging classroom experience. This article examines the complete technical pipeline as implemented in the THU-MAIC/OpenMAIC repository, including the specific packages, APIs, and configuration points that make this transformation possible.

The Six-Stage Transformation Pipeline

Stage 1: Document Import and Normalization

The transformation begins in @openmaic/importer, where raw files are parsed and normalized into the MAIC slide DSL (Slide[]).

The package exposes two primary entry points:

  • importPptx(file, options) — for PowerPoint files
  • parse(file) — for PDF and plain text documents

During import, the system:

  • Extracts text, layout, and embedded media from source files
  • Normalizes all content into a standardized slide structure
  • Optionally uploads media assets to user-provided object storage via the upload callback

In packages/@openmaic/importer/README.md, the import contract specifies that all slide geometry, typography, and asset references must conform to the Slide TypeScript interface, ensuring downstream components receive predictable data.

Stage 2: Document Storage and Persistence

Once normalized, slides are persisted as a stage document (MaicDocument) through @openmaic/storage.

The storage layer provides:

  • Contract-first design — HTTP contracts defined in packages/@openmaic/storage/docs/ guarantee JSON-safe payloads
  • Multiple backends — PostgreSQL for server-side deployments, IndexedDB for browser environments
  • Immutable stage IDs — each document receives a unique stageId that serves as the canonical reference for all subsequent operations

The MaicDocument schema includes:

  • outline — structural metadata for the lesson
  • scenes — individual slides with canvas content
  • assets — media references and generation placeholders
  • runtime — session state and progress tracking

According to packages/@openmaic/storage/README.md, the DocumentStore.putStage() method guarantees atomic writes, ensuring data consistency during concurrent generation jobs.

Stage 3: AI-Powered Content Generation

The generation engine (@openmaic/generation) orchestrates a multi-stage LLM pipeline through the /api/generate-classroom endpoint.

The generation workflow follows a two-stage architecture:

Phase Description Output
Outline creation LLM analyzes document structure and learning objectives Hierarchical lesson outline with scene boundaries
Scene generation Parallel generation of content, quizzes, and media per scene Populated scenes with interactive elements

Each scene may trigger additional AI services:

  • Text completion — elaborated explanations, discussion prompts
  • Quiz generation — multiple-choice, fill-in-blank, coding exercises
  • Image generation — diagrams, illustrations, scene backgrounds
  • Text-to-speech — narrated content for accessibility

All prompt templates reside in packages/@openmaic/generation/templates/ and are selected based on the DEFAULT_MODEL environment variable. Feature flags (enableWebSearch, enableImageGeneration, enableTTS) control which capabilities are activated per request, as documented in skills/openmaic/references/generate-flow.md.

Stage 4: Interactive Rendering

The @openmaic/renderer package converts stored MaicDocument instances into running React applications.

Key rendering capabilities include:

  • Canvas-based slide display with drag-and-drop interaction
  • Embedded widgets — quizzes, code playgrounds, whiteboards
  • Video and audio playback with synchronized transcripts
  • Skeleton placeholders for asynchronously generated media

The renderer implements a progressive loading strategy: when a slide references a media asset still being generated, it displays a skeleton UI using the elementId stored in the document, then hot-swaps the final asset upon completion. This pattern is defined in packages/@openmaic/renderer/README.md.

Stage 5: Runtime Manipulation and Live Editing

During active classroom sessions, the agent-runtime layer (skills/agent-runtime/stage-dsl/) provides programmatic access to stage documents.

Three core utilities enable live interaction:

  • read_stage(stageId) — Retrieves current document state
  • patch_stage(stageId, operations) — Applies atomic modifications to scenes or metadata
  • grep_stage(stageId, query) — Searches content across all scenes

These primitives power features like:

  • Real-time instructor annotations
  • Student progress tracking
  • Dynamic content adaptation based on quiz responses

The SKILL.md file in skills/agent-runtime/stage-dsl/ specifies the complete operation schema and concurrency guarantees for these runtime utilities.

Stage 6: End-to-End Orchestration

The complete document-to-classroom flow integrates all prior stages:


User uploads document
    ↓
importPptx() / parse() — Slide[] extraction
    ↓
DocumentStore.putStage() — persistence with stageId
    ↓
POST /api/generate-classroom — async generation job
    ↓
Poll /api/job/{jobId} — monitor until completion
    ↓
MaicRenderer — interactive lesson display

This orchestration pattern is illustrated in the generation pipeline diagram located in the repository's top-level README.md, under the #generation section.

Modular Architecture and Extension Points

Package-Based Design

Each pipeline stage is published as an independent npm package under @openmaic/*:

Package Responsibility Install Command
@openmaic/importer File parsing and normalization npm i @openmaic/importer
@openmaic/storage Document persistence contracts npm i @openmaic/storage
@openmaic/generation LLM orchestration and prompts npm i @openmaic/generation
@openmaic/renderer React-based interactive output npm i @openmaic/renderer
@openmaic/dsl Shared type definitions npm i @openmaic/dsl

This structure enables selective adoption — a custom learning management system might import and store documents directly while implementing its own rendering layer.

Provider Configuration

The generation pipeline accepts environment-based configuration for pluggable AI services:


# Required

DEFAULT_MODEL=anthropic/claude-3-sonnet

# Optional capabilities

ENABLE_WEB_SEARCH=true
ENABLE_IMAGE_GENERATION=true
ENABLE_TTS=true
ENABLE_VIDEO_GENERATION=false

# Provider-specific keys

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-...
REPLICATE_API_TOKEN=r8_...

Configuration details are maintained in provider-keys.md within the skills documentation.

Complete Implementation Example

The following TypeScript demonstrates the full transformation pipeline in a Node.js environment:

// 1️⃣ Import and normalize a PowerPoint file
import { importPptx } from '@openmaic/importer';
import type { Slide } from '@openmaic/importer';

async function loadSlides(file: Buffer): Promise<Slide[]> {
  return await importPptx(file, {
    upload: async (blob) => {
      const url = await uploadToOss(blob);
      return url;
    },
  });
}
// 2️⃣ Persist as a stage document
import { DocumentStore } from '@openmaic/storage/document/pg';
import { MaicDocument } from '@openmaic/dsl';

async function createStage(slides: Slide[]): Promise<string> {
  const stageId = `stage-${Date.now()}`;
  const doc: MaicDocument = {
    id: stageId,
    name: 'Interactive Lesson',
    dslVersion: 'v1',
    outline: { objectives: [], duration: 45 },
    scenes: slides.map((s, i) => ({
      id: `scene-${i}`,
      content: { canvas: s },
    })),
  };
  await DocumentStore.putStage(doc);
  return stageId;
}
// 3️⃣ Initiate generation and poll for completion
import fetch from 'node-fetch';

async function generateAndRender(stageId: string) {
  const { jobId } = await fetch(`${OPENMAIC_URL}/api/generate-classroom`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      stageId,
      enableImageGeneration: true,
      enableTTS: true,
    }),
  }).then(r => r.json());

  // Poll until completion
  while (true) {
    const { status, result } = await fetch(
      `${OPENMAIC_URL}/api/job/${jobId}`
    ).then(r => r.json());

    if (status === 'completed') return result.stageId;
    if (status === 'failed') throw new Error('Generation failed');
    await new Promise(res => setTimeout(res, 60000));
  }
}
// 4️⃣ Render in a React application
import { MaicRenderer } from '@openmaic/renderer';

function Classroom({ stageId }: { stageId: string }) {
  const [doc, setDoc] = React.useState(null);

  React.useEffect(() => {
    DocumentStore.getStage(stageId).then(setDoc);
  }, [stageId]);

  return doc ? <MaicRenderer document={doc} /> : <p>Loading…</p>;
}

Summary

  • OpenMAIC implements a six-stage pipeline — import, store, generate, render, runtime manipulation, and end-to-end orchestration — that transforms static documents into interactive classrooms.
  • The @openmaic/* monorepo packages provide modular, contract-guaranteed components that can be adopted individually or as a complete system.
  • Two-stage generation enables parallel content creation with fine-grained retry logic, while skeleton placeholders ensure responsive UI during asynchronous media generation.
  • Immutable stage documents with unique stageId references provide the foundational data model for persistence, generation tracking, and runtime editing.
  • Provider-configurable AI services allow deployment-specific selection of LLMs, image models, and voice synthesis without code changes.

Frequently Asked Questions

What file formats does OpenMAIC support for document import?

OpenMAIC currently supports PowerPoint (PPTX), PDF, and plain text documents. The @openmaic/importer package provides importPptx() for PPTX files and parse() for PDF and text files. Both functions normalize content into the same Slide[] DSL structure, ensuring consistent downstream processing regardless of source format.

How does OpenMAIC handle media generation that takes longer than the HTTP timeout?

The generation pipeline returns immediately with a jobId that clients poll via /api/job/{jobId}. During generation, the renderer displays skeleton placeholders using elementId references stored in the document. When media completes, the final URL is patched into the stage document and the renderer hot-swaps the placeholder. This pattern decouples generation latency from user experience.

Can OpenMAIC integrate with custom storage backends beyond PostgreSQL and IndexedDB?

Yes. The storage contracts in packages/@openmaic/storage/docs/ define the interface that any backend must implement. The package provides reference implementations for PostgreSQL (@openmaic/storage/document/pg) and IndexedDB (@openmaic/storage/document/idb), but developers can implement the same contract for Redis, MongoDB, or proprietary systems. The contract-first design ensures that front-end components remain compatible regardless of backend choice.

What control do educators have over the AI-generated content?

Educators influence generation through feature flags (enableImageGeneration, enableTTS, etc.) and model selection (DEFAULT_MODEL). The prompt templates in packages/@openmaic/generation/templates/ can be customized for institution-specific pedagogy. Additionally, the patch_stage runtime utility allows live editing of generated content before or during classroom delivery, and the stage-dsl SKILL documentation defines how to programmatically constrain or guide generation through the outline structure.

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 →