# How to Achieve Parallel Scene Content Generation in OpenMAIC: A Complete Technical Guide

> Unlock parallel scene content generation in OpenMAIC. Learn how its three-layer architecture enables simultaneous LLM/image model calls and streams results efficiently.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: how-to-guide
- Published: 2026-08-30

---

**OpenMAIC enables parallel scene content generation by dispatching independent async generation jobs for each scene through a three-layer architecture—UI triggers, per-scene Zustand state tracking, and server-side concurrent job queues—allowing multiple LLM/image model calls to execute simultaneously while streaming results back to the client.**

OpenMAIC is an open-source framework for multi-modal AI content creation that handles resource-intensive generation tasks across multiple scenes concurrently. This article explains how to implement parallel scene content generation by leveraging the repository's fire-and-forget pipeline, which decouples UI requests from server-side execution to maximize throughput.

## Understanding the Three-Layer Architecture

OpenMAIC separates concerns into three distinct layers to handle concurrency safely and efficiently:

- **UI Trigger Layer**: Components like [`components/generation/interactive-mode-button.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/generation/interactive-mode-button.tsx) initiate generation without blocking the main thread.
- **Client Store Layer**: The Zustand store in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) maintains isolated state for every `sceneId` using `MediaGenerationTask` objects.
- **Server Worker Layer**: The [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts) file defines the `runGeneration` function that executes inside a configurable job queue, processing many requests simultaneously up to `MAX_CONCURRENT_JOBS`.

## Dispatching Fire-and-Forget Requests from the UI

To initiate parallel generation, the UI maps over a list of scene IDs and invokes the generation function for each without awaiting individual results. This pattern prevents serialization bottlenecks.

When a user clicks the generate button, the component fires off all requests immediately:

```typescript
// components/generation/interactive-mode-button.tsx
import { generateScene } from '@/lib/server/agent-runtime/generation-tools';

const startParallel = async (sceneIds: string[]) => {
  // Create an array of promises, one per scene
  const promises = sceneIds.map(id => generateScene(id));
  // Optionally await all completions for a final "complete" signal
  await Promise.all(promises);
};

```

Each call to `generateScene` returns immediately after registering the task, allowing the loop to proceed to the next scene without waiting for the model to finish.

## Tracking Per-Scene State with Zustand

The client-side store isolates each generation task by indexing state with `sceneId`. This prevents collisions when multiple scenes update concurrently.

In [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts), the store creates a `MediaGenerationTask` for every request:

```typescript
// lib/store/media-generation.ts
import { create } from 'zustand';

type MediaGenerationTask = {
  token: string;
  status: 'open' | 'complete' | 'error';
  prompt: string;
};

type MediaGenState = {
  tasks: Record<string, MediaGenerationTask>;
  startGeneration: (opts: { sceneId: string; prompt: string }) => void;
};

export const useMediaGenerationStore = create<MediaGenState>(set => ({
  tasks: {},
  startGeneration: ({ sceneId, prompt }) => {
    const token = crypto.randomUUID();
    set(state => ({
      tasks: { 
        ...state.tasks, 
        [sceneId]: { token, status: 'open', prompt } 
      },
    }));
    // Dispatch to server without awaiting
    api.runGeneration({ sceneId, token, prompt });
  },
}));

```

Because the `tasks` object uses `sceneId` as the key, updates to one scene never overwrite another, enabling safe parallel state mutations.

## Executing Concurrent Jobs on the Server

The server processes generation requests through an async job queue that respects concurrency limits. Each `runGeneration` call runs in its own execution context, allowing the event loop to handle multiple model inferences simultaneously.

According to the OpenMAIC source code in [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts), the server implementation looks like this:

```typescript
// lib/server/agent-runtime/generation-tools.ts
import { MAX_CONCURRENT_JOBS } from '@/configs/concurrency';

export async function runGeneration({
  sceneId,
  token,
  prompt,
}: { sceneId: string; token: string; prompt: string }) {
  // The job queue manages concurrency automatically
  await jobQueue.add(async () => {
    const result = await model.generate(prompt);
    // Stream partial or final results back to client
    pushUpdate(sceneId, { token, content: result });
  });
}

```

The `MAX_CONCURRENT_JOBS` configuration variable controls how many of these async jobs run at once. Once a job completes, the queue automatically pulls the next waiting task, maintaining optimal throughput without overwhelming the GPU or API rate limits.

## Streaming Results to the Stage Store

As the server generates content, it pushes updates via WebSocket or Server-Sent Events (SSE). The client listens for these updates and patches the corresponding scene in the stage store.

The media generation store listens for `generation:update` events and routes them to the correct scene:

```typescript
// lib/store/media-generation.ts
socket.on('generation:update', ({ sceneId, delta }) => {
  useStageStore.getState().updateSceneContent(sceneId, delta);
});

```

The **stage store** ([`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts)) holds the canonical scene data. Because updates are keyed by `sceneId`, the UI components—such as those in [`components/stage/scene-renderer.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/stage/scene-renderer.tsx)—re-render only when their specific scene content changes, ensuring efficient DOM updates across many parallel generations.

## Complete Implementation Example

Here is a minimal end-to-end implementation demonstrating parallel generation for a batch of scenes:

```tsx
// components/generation/batch-generator.tsx
import { generateScene } from '@/lib/server/agent-runtime/generation-tools';
import { useMediaGenerationStore } from '@/lib/store/media-generation';
import { Button } from '@/components/ui/button';

export const BatchGenerator = ({ sceneIds }: { sceneIds: string[] }) => {
  const startBatch = async () => {
    // Fire all generation requests simultaneously
    const jobs = sceneIds.map(id => 
      generateScene(id)
    );
    
    // Wait for all to settle (optional, for "Export" or "Next" gating)
    await Promise.all(jobs);
  };

  return <Button onClick={startBatch}>Generate All Scenes</Button>;
};

```

This button triggers parallel execution, while the underlying stores and server queue handle the complexity of concurrency management and state isolation.

## Summary

- **Parallel dispatch** is achieved by mapping scene IDs to generation promises without sequential awaiting, as implemented in [`components/generation/interactive-mode-button.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/generation/interactive-mode-button.tsx).
- **State isolation** relies on Zustand stores keyed by `sceneId` in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts), using `MediaGenerationTask` objects to track individual progress.
- **Server concurrency** is managed through an async job queue in [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts), respecting the `MAX_CONCURRENT_JOBS` limit while running multiple `runGeneration` calls simultaneously.
- **Live synchronization** occurs via WebSocket updates that patch the stage store ([`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts)), ensuring each scene updates independently without UI blocking.

## Frequently Asked Questions

### How does OpenMAIC prevent race conditions when updating scene content?

OpenMAIC prevents race conditions by indexing all generation state and scene content by `sceneId`. The Zustand store in [`lib/store/media-generation.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/media-generation.ts) uses a dictionary structure where each scene has its own `MediaGenerationTask` entry, and the stage store applies updates only to the matching `sceneId`. This isolation ensures that concurrent updates for different scenes never corrupt each other's data.

### What configuration controls the maximum number of concurrent generations?

The `MAX_CONCURRENT_JOBS` variable in the concurrency configuration file controls server-side parallelism. This setting defines how many `runGeneration` jobs can execute simultaneously in the job queue defined in [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts). Adjusting this value allows you to optimize for your hardware capacity or API rate limits.

### How does the UI know when all parallel generations are complete?

The UI can track completion by awaiting `Promise.all` on the array of generation promises returned by `generateScene`, as shown in [`components/generation/interactive-mode-button.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/generation/interactive-mode-button.tsx). Alternatively, components can subscribe to the `useMediaGenerationStore` and check that all relevant `sceneId` entries have a status of `complete` or `error` before enabling downstream actions like navigation or export.

### Can different scene types be generated in parallel?

Yes, the architecture supports heterogeneous scene types because each generation job is context-independent. Whether generating slides, images, or video segments, the `runGeneration` function in [`lib/server/agent-runtime/generation-tools.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/server/agent-runtime/generation-tools.ts) executes each request in its own async context with a unique `token`. The [`components/stage/scene-renderer.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/stage/scene-renderer.tsx) component then dispatches to the appropriate renderer based on the scene type once content arrives.