How OpenMAIC Generates and Uses SceneOutline Objects in Its Two-Stage Pipeline
OpenMAIC creates SceneOutline objects through an LLM-powered streaming pipeline that converts user requirements into structured course blueprints, then consumes these outlines to drive full content generation and UI navigation.
In OpenMAIC, a SceneOutline serves as the foundational data structure that bridges high-level user requirements and executable course content. This article explains exactly how these outlines are generated, parsed, and utilized throughout the platform's architecture, drawing directly from the THU-MAIC/OpenMAIC source code.
What Is a SceneOutline in OpenMAIC?
A SceneOutline is a TypeScript interface defined in lib/types/generation.ts that represents a single unit of course content—whether a slide, quiz, interactive widget, or project-based learning (PBL) module. The interface captures pedagogical intent without committing to final media assets or interactive implementations.
The full schema includes:
export interface SceneOutline {
id: string;
type: 'slide' | 'quiz' | 'interactive' | 'pbl';
title: string;
description: string;
keyPoints: string[];
teachingObjective?: string;
estimatedDuration?: number;
order: number;
languageNote?: string;
suggestedImageIds?: string[];
mediaGenerations?: MediaGenerationRequest[];
quizConfig?: { questionCount: number; difficulty: 'easy'|'medium'|'hard'; questionTypes: ('single'|'multiple'|'text')[] };
interactiveConfig?: { conceptName: string; conceptOverview: string; designIdea: string; subject?: string };
pblConfig?: { projectTopic: string; projectDescription: string; targetSkills: string[]; issueCount?: number; scenarioRoleplay?: boolean; scenarioBrief?: string };
widgetType?: WidgetType;
widgetOutline?: WidgetOutline;
}
Stage 1: Generating SceneOutline Objects from Requirements
Input and Prompt Construction
The generation process begins when the system receives UserRequirements plus any uploaded source documents. These inputs are merged with a specialized prompt located at:
packages/@openmaic/generation/templates/requirements-to-outlines/system.md
This prompt instructs the LLM to return a JSON structure containing:
- A
languageDirectivespecifying the target language - A
courseTitlefor the generated course - An
outlinesarray where each element conforms to theSceneOutlineinterface
Streaming LLM Response and Incremental Parsing
The prompt is dispatched to the configured LLM via streamLLM in app/api/generate/scene-outlines-stream/route.ts. Critically, OpenMAIC does not wait for the complete response. Instead, it implements an O(n) incremental parser that extracts SceneOutline objects as they arrive.
Three extraction functions operate on the streaming buffer:
extractLanguageDirective— isolates the language directiveextractCourseTitle— captures the course titleextractNewOutlines— scans the buffer once, identifies complete JSON objects within theoutlinesarray, parses them withJSON.parse, and returns both theSceneOutlineobjects and the next scan position
This single-pass approach minimizes memory overhead and latency for large outline sets.
SSE Streaming to the Frontend
For each parsed outline, the route emits a Server-Sent Events (SSE) payload:
{ "type": "outline", "data": <SceneOutline>, "index": <number> }
The client consumes these events progressively. Upon completion, a final done event delivers the complete array:
{ "type": "done", "data": { "outlines": SceneOutline[] } }
Client-side consumption example:
const evtSource = new EventSource('/api/generate/scene-outlines-stream');
const outlines: SceneOutline[] = [];
evtSource.addEventListener('outline', (e: MessageEvent) => {
const payload = JSON.parse(e.data) as { data: SceneOutline; index: number };
outlines[payload.index] = payload.data;
console.log('Received outline', payload.index, payload.data.title);
});
evtSource.addEventListener('done', (e: MessageEvent) => {
const result = JSON.parse(e.data) as { outlines: SceneOutline[] };
evtSource.close();
});
Stage 2: Consuming SceneOutline Objects for Full Generation
Once the outline array is complete, OpenMAIC feeds it to the second generation stage in @openmaic/generation. This stage expands each SceneOutline into concrete content:
| Outline Type | Expansion Behavior |
|---|---|
slide |
Generates DSL markup with text layouts and image placeholders |
quiz |
Creates question objects based on quizConfig parameters |
interactive |
Builds widget configurations using interactiveConfig and widgetOutline |
pbl |
Constructs project scenarios from pblConfig specifications |
Runtime Usage of SceneOutline Objects
The generated outlines persist across the application through several mechanisms:
- Session Storage:
lib/workbench/workspace-session-memory.tsmaintains the outline list in active workspace state - UI Rendering: Outline titles and key points drive the course outline view and navigation controls
- Media Prefetching:
mediaGenerationsarrays trigger background asset generation - Widget Integration: Interactive outlines feed directly into the widget system via
widgetTypeandwidgetOutline
Manual construction for testing:
import { SceneOutline } from '@/lib/types/generation';
const demoOutline: SceneOutline = {
id: 'outline_001',
type: 'slide',
title: 'Introduction to Photosynthesis',
description: 'Explain the basic process and why it matters.',
keyPoints: ['Light absorption', 'Carbon fixation', 'Oxygen release'],
order: 0,
languageNote: 'English',
suggestedImageIds: ['img_12'],
};
Implementation Files and Architecture
| File | Purpose |
|---|---|
lib/types/generation.ts |
Type definitions for SceneOutline and related interfaces |
app/api/generate/scene-outlines-stream/route.ts |
SSE endpoint with incremental JSON parsing |
packages/@openmaic/generation/templates/requirements-to-outlines/system.md |
LLM prompt template for outline generation |
lib/workbench/workspace-session-memory.ts |
Session storage for outline persistence |
lib/workbench/workspace-panes.ts |
UI components consuming outline data |
Summary
SceneOutlinegeneration starts with requirements fed through a templated LLM prompt, producing structured JSON via streaming response- Incremental parsing in
route.tsachieves O(n) efficiency by extracting complete objects without buffering the entire response - SSE streaming enables progressive UI updates as outlines materialize
- Two-stage architecture separates pedagogical planning (outlines) from content execution (full scenes)
- Multi-context consumption spans session storage, navigation, media generation, and widget instantiation throughout the OpenMAIC workbench
Frequently Asked Questions
What triggers SceneOutline generation in OpenMAIC?
SceneOutline generation begins when a user submits requirements through the workbench interface, either directly as text input or combined with uploaded documents (PDF, DOCX). The system packages these into UserRequirements and initiates the streaming API call to /api/generate/scene-outlines-stream.
Why does OpenMAIC use incremental parsing instead of waiting for the full LLM response?
Incremental parsing eliminates latency for large courses and reduces memory pressure. The extractNewOutlines function in app/api/generate/scene-outlines-stream/route.ts processes the response buffer in a single pass, emitting valid SceneOutline objects immediately. This allows users to see and interact with early outlines while generation continues.
How do SceneOutline objects differ from final scene content?
A SceneOutline captures intent and structure without executable implementation. It specifies what to teach (title, description, keyPoints, objectives) and how (type-specific configs), but lacks rendered slides, actual quiz questions, or compiled widgets. Stage 2 expands these blueprints into concrete, runnable content.
Can SceneOutline objects be manually created or edited?
Yes, the TypeScript interface in lib/types/generation.ts permits manual construction, and the session-based architecture in workspace-session-memory.ts accepts outline modifications. This enables hybrid workflows where users augment LLM-generated outlines with custom entries before stage 2 generation executes.
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 →