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 filesparse(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
uploadcallback
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
stageIdthat serves as the canonical reference for all subsequent operations
The MaicDocument schema includes:
outline— structural metadata for the lessonscenes— individual slides with canvas contentassets— media references and generation placeholdersruntime— 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 statepatch_stage(stageId, operations)— Applies atomic modifications to scenes or metadatagrep_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
stageIdreferences 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →