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 languageDirective specifying the target language
  • A courseTitle for the generated course
  • An outlines array where each element conforms to the SceneOutline interface

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 directive
  • extractCourseTitle — captures the course title
  • extractNewOutlines — scans the buffer once, identifies complete JSON objects within the outlines array, parses them with JSON.parse, and returns both the SceneOutline objects 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.ts maintains the outline list in active workspace state
  • UI Rendering: Outline titles and key points drive the course outline view and navigation controls
  • Media Prefetching: mediaGenerations arrays trigger background asset generation
  • Widget Integration: Interactive outlines feed directly into the widget system via widgetType and widgetOutline

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

  • SceneOutline generation starts with requirements fed through a templated LLM prompt, producing structured JSON via streaming response
  • Incremental parsing in route.ts achieves 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:

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 →