How the `generateSceneContent` Function Works in OpenMAIC: Deep Dive into Lesson Content Generation
The generateSceneContent function in OpenMAIC acts as a unified dispatcher that routes scene outlines to specialized generators based on type, handling slides, quizzes, interactive widgets, and PBL lessons through a single async API.
generateSceneContent serves as the core entry point of the @openmaic/generation package. Located in packages/@openmaic/generation/src/scene-generator.ts, it transforms high-level SceneOutline objects into fully rendered lesson content by delegating to type-specific generators. This architecture enables consistent content generation across multiple interfaces—the server-side route /api/scene-generation, the React hook useSceneGenerator, and the CLI script scripts/generation-node-smoke.mjs.
Function Signature and Core Responsibilities
The function accepts three parameters that define its flexible contract:
outline– ASceneOutlinedescribing the target output (slide, quiz, interactive widget, or PBL lesson)aiCall– An async function that interfaces with the configured LLMoptions– Optional configuration controlling image handling, language directives, procedural skills, and logging
// Simplified signature from scene-generator.ts
async function generateSceneContent(
outline: SceneOutline,
aiCall: AICallFunction,
options?: GenerationOptions
): Promise<GeneratedContent | null>
The implementation demonstrates clean separation of concerns: common setup, type-based routing, and specialized generation pipelines.
Common Setup and Configuration Phase
Before any generation begins, the function establishes its execution context. Lines 27-52 in scene-generator.ts handle this initialization:
// From packages/@openmaic/generation/src/scene-generator.ts#L27-L52
const logger = options.logger ?? noopGenerationLogger;
const {
imageMapping,
generatedMediaMapping,
visionEnabled,
languageDirectives,
allowProceduralSkill,
// ... additional destructured options
} = options ?? {};
This phase ensures that all downstream generators have consistent access to logging, media resolution maps, and feature flags. The nullish coalescing pattern (??) guarantees safe defaults without polluting call sites.
Interactive Widget Generation Pipeline
When outline.type === 'interactive', the function executes a dedicated migration and dispatch path (lines 54-80).
Legacy Configuration Migration
Older outlines containing interactiveConfig objects are automatically upgraded:
// From packages/@openmaic/generation/src/scene-generator.ts#L54-L80
if (outline.interactiveConfig) {
outline = convertInteractiveConfigToWidget(outline);
}
The convertInteractiveConfigToWidget helper bridges API evolution without breaking existing consumers.
Widget Type Resolution and Fallback
If the migrated outline still lacks a widgetType, the system applies backward-compatible defaults:
- Missing
widgetType→ defaults to 'simulation'
This ensures that legacy content continues to render predictably.
Delegation to generateWidgetContent
The final step routes to generateWidgetContent, which handles all five supported widget types:
| Widget Type | Description |
|---|---|
simulation |
Interactive physics or system simulations |
diagram |
Editable or explorable diagrams |
code |
Executable code playgrounds |
game |
Educational mini-games |
visualization3d |
Three-dimensional data or model viewers |
// Widget generation delegation
return generateWidgetContent(outline, aiCall, {
logger,
visionEnabled,
languageDirectives,
});
Non-Interactive Content Generation
For non-interactive outlines, a switch statement on outline.type determines the appropriate generator (lines 82-110).
Slide Generation via generateSlideContent
generateSlideContent handles the most complex non-interactive path. According to the source, it manages:
- Image insertion and resolution – mapping placeholder IDs to actual URLs
- Video embedding – with normalized references
- LaTeX rendering – converting mathematical expressions to HTML
- Element defaults – applying DSL normalization and aspect ratio corrections
Key helper functions support this pipeline:
resolveImageIds– replacesimg_1,img_2placeholders with mapped URLsnormalizeGeneratedVideoRefs– sanitizes video reference structuresprocessLatexElements– renders LaTeX via KaTeX with malformed-element removalfixElementDefaults– reconciles generated elements against PDF asset dimensions
// Simplified slide generation flow
case 'slide':
const slideContent = await generateSlideContent(outline, aiCall, options);
return fixElementDefaults(
resolveImageIds(slideContent, imageMapping),
generatedMediaMapping
);
Quiz Generation via generateQuizContent
Quiz outlines route to generateQuizContent, which produces structured question-answer objects. This generator focuses on pedagogical correctness and answer key accuracy rather than media handling.
PBL Lesson Generation via generatePBLSceneContent
Problem-Based Learning (PBL) scenes follow a more complex orchestration:
// From packages/@openmaic/generation/src/scene-generator.ts#L100-L110
case 'pbl':
return generatePBLSceneContent(outline, aiCall, {
logger,
allowProceduralSkill,
// PBL-specific options
});
The PBL generator optionally falls back to a loop-based planning mode when initial generation requires refinement, enabling iterative lesson structure development.
Unknown Type Handling
Unrecognized outline types return null, signaling to callers that no content could be generated. This explicit failure mode supports graceful degradation in production pipelines.
Media Resolution and Post-Processing Architecture
Several internal functions standardize content across all generation paths:
resolveImageIds and normalizeGeneratedVideoRefs
These utilities bridge the gap between LLM-generated placeholders and actual media assets:
// Conceptual usage across generators
const resolvedContent = resolveImageIds(
rawGeneratedContent,
imageMapping // Maps "img_1" → "https://cdn.example.com/actual-image.png"
);
fixElementDefaults
This helper enforces DSL consistency by:
- Calling
normalizeElementfor each generated element - Adjusting image aspect ratios to match actual PDF-derived assets
- Ensuring coordinate and sizing defaults align with rendering requirements
processLatexElements
Mathematical content undergoes server-side rendering:
- KaTeX converts LaTeX strings to performant HTML
- Malformed or unparseable expressions are removed to prevent client-side crashes
Practical Usage Examples
Node.js Script Direct Execution
// scripts/generation-node-smoke.mjs
import { generateSceneContent } from '@openmaic/generation';
import { slideOutline } from './fixtures/slide-outline';
import { aiCall } from './ai-call-mock';
async function demo() {
const content = await generateSceneContent(
slideOutline(),
aiCall,
{
logger: console,
visionEnabled: false,
allowProceduralSkill: true,
},
);
console.log(content);
}
demo();
This pattern supports CI/CD smoke testing and batch content generation workflows.
Server-Side API Route Integration
// lib/server/scene-generation.ts
export async function sceneGenerationHandler(req, res) {
const { outline } = req.body;
const aiCall = makeAICall(req);
const content = await generateSceneContent(outline, aiCall, {
logger: requestLogger,
imageMapping: req.imageMapping,
});
res.json({ content });
}
The server context propagates request-specific media mappings and authentication-bound AI callers.
React Hook Abstraction
// hooks/use-scene-generator.ts
import { useCallback } from 'react';
import { generateSceneContent } from '@openmaic/generation';
import { useAiCall } from '../hooks/useAiCall';
export function useSceneGenerator() {
const aiCall = useAiCall();
return useCallback(
(outline, opts) => generateSceneContent(outline, aiCall, opts),
[aiCall],
);
}
This hook pattern enables declarative scene generation within React components while maintaining proper dependency management.
Key Source Files Reference
| File Path | Role in generateSceneContent Architecture |
|---|---|
packages/@openmaic/generation/src/scene-generator.ts |
Core implementation with dispatch logic and helper functions |
packages/@openmaic/generation/src/scene-types.ts |
TypeScript interfaces for SceneOutline, GenerationOptions, and return types |
packages/@openmaic/generation/src/widget-generator.ts |
Contains generateWidgetContent for interactive content |
lib/server/scene-generation.ts |
Production server endpoint consuming the function |
hooks/use-scene-generator.ts |
React integration layer |
scripts/generation-node-smoke.mjs |
Standalone execution example |
Summary
generateSceneContentcentralizes lesson content generation in OpenMAIC through a type-dispatched architecture- The function handles four major outline types: interactive widgets, slides, quizzes, and PBL lessons
- Interactive content migrates legacy configs and delegates to
generateWidgetContentfor five widget variants - Non-interactive content uses explicit switch-based routing to specialized generators with rich post-processing
- Media resolution helpers (
resolveImageIds,normalizeGeneratedVideoRefs,fixElementDefaults,processLatexElements) ensure LLM output matches production asset realities - The unified API surface supports server routes, React hooks, and CLI scripts without code duplication
Frequently Asked Questions
What happens when generateSceneContent receives an unknown outline type?
The function returns null to signal that no generator exists for the provided type. This explicit null return allows callers to implement fallback behaviors or log diagnostic information about unsupported outline configurations.
Does generateSceneContent handle image generation directly?
No. The function receives pre-existing images through imageMapping and generatedMediaMapping parameters. It resolves placeholder IDs (like img_1) to actual URLs but delegates actual image creation to upstream AI call implementations or external services.
How does backward compatibility work for interactive content?
Legacy outlines containing interactiveConfig objects are automatically migrated via convertInteractiveConfigToWidget. Additionally, outlines without an explicit widgetType default to 'simulation', ensuring older content continues to function without manual updates.
Can generateSceneContent operate without a logger?
Yes. The function uses nullish coalescing (options.logger ?? noopGenerationLogger) to substitute a no-op logger when none is provided. This eliminates mandatory logging configuration while preserving structured logging capabilities for production deployments.
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 →