# How OpenMAIC Generates and Uses SceneOutline Objects in Its Two-Stage Pipeline

> Discover how OpenMAIC generates SceneOutline objects using an LLM pipeline to create structured course blueprints and drive content generation. Learn about its two-stage process.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-03

---

**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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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:

```ts
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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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:

```json
{ "type": "outline", "data": <SceneOutline>, "index": <number> }

```

The client consumes these events progressively. Upon completion, a final `done` event delivers the complete array:

```json
{ "type": "done", "data": { "outlines": SceneOutline[] } }

```

**Client-side consumption example:**

```ts
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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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:**

```ts
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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts) | Type definitions for `SceneOutline` and related interfaces |
| [`app/api/generate/scene-outlines-stream/route.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/workbench/workspace-session-memory.ts) | Session storage for outline persistence |
| [`lib/workbench/workspace-panes.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/generation.ts) permits manual construction**, and the session-based architecture in [`workspace-session-memory.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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.