Generation Contracts and Pipeline Definitions in @openmaic/generation: A Complete Reference
The @openmaic/generation package provides TypeScript contracts for retry semantics, result handling, and type-safe data structures that orchestrate AI-driven content generation in the OpenMAIC framework.
These generation contracts and pipeline definitions form the foundation for building reliable, extensible AI generation workflows. In pipeline-types.ts and generation-retry.ts, the OpenMAIC team defines interfaces that decouple your application code from specific LLM implementations while enforcing consistent error handling and data validation.
Generation Contracts: Retry Semantics and Result Handling
Generation contracts control how the system responds to failures, rate limits, and transient errors. They abstract retry logic into reusable, testable primitives.
GenerationRetryEvent: Observability for Every Attempt
The GenerationRetryEvent interface in generation-retry.ts exposes metadata for logging, metrics, or UI feedback during retry operations.
export interface GenerationRetryEvent {
label: string; // Human-readable identifier (e.g. "Slide generation")
attempt: number; // Current attempt count (starting at 1)
maxAttempts: number; // Total attempts allowed (retries + first try)
nextDelayMs: number; // Back-off delay before the next attempt
reason: string; // Human-readable cause (e.g. "HTTP 429")
}
Source: packages/@openmaic/generation/src/generation-retry.ts (lines 1-6)
Each field serves a specific purpose. The attempt counter helps detect flapping services, while nextDelayMs enables progress indicators in user interfaces.
GenerationRetryOptions: Configurable Retry Behavior
The GenerationRetryOptions<T> interface in generation-retry.ts accepts a generic type parameter that flows through to the result predicate, enabling type-safe validation of retry-worthy outcomes.
export interface GenerationRetryOptions<T> {
label: string; // Identifier shown in logs / events
maxRetries?: number; // Maximum retry count (default 5)
baseDelayMs?: number; // Initial back-off delay (default 1000 ms)
maxDelayMs?: number; // Upper bound for back-off (default 16000 ms)
signal?: AbortSignal; // Allows external cancellation
sleep?: (ms, signal?) => Promise; // Custom sleep implementation (e.g. test mock)
random?: () => number; // Custom random source for jitter
shouldRetryResult?: (result: T) => boolean; // Optional result predicate
onRetry?: (event: GenerationRetryEvent) => Promise<void> | void;
}
Source: packages/@openmaic/generation/src/generation-retry.ts (lines 9-18)
Key design decisions in this contract:
- Injectability: The
sleepandrandomparameters enable deterministic testing without monkey-patching globals. - AbortSignal integration: Native cancellation support prevents wasted compute when users navigate away or requests time out.
- Result-based retries: The
shouldRetryResultcallback handles "successful" HTTP 200 responses with malformed or incomplete AI output.
GenerationResult: Uniform Output Wrapper
Every pipeline step returns a GenerationResult<T> defined in pipeline-types.ts, establishing a consistent pattern for success and failure cases.
export interface GenerationResult<T> {
success: boolean; // `true` when the operation succeeded
data?: T; // Payload on success
error?: string; // Human-readable error message on failure
}
Source: packages/@openmaic/generation/src/pipeline-types.ts (lines 54-58)
This structure replaces thrown exceptions with explicit error propagation, making control flow predictable and serialization safe for distributed systems.
Core Helper Functions
Three functions in generation-retry.ts implement the retry contract:
withGenerationRetry– Executes async operations with exponential back-off, jitter, and abort awareness.isRetryableGenerationError– Classifies errors (network failures, HTTP 5xx, rate limits) as retryable or fatal.isAbortError– Normalizes abort detection across browserDOMException, NodeAbortError, and testing environments.
These exports are available from the package entry point at packages/@openmaic/generation/src/index.ts.
Pipeline Definitions: Data Structures for AI Orchestration
Pipeline definitions in pipeline-types.ts describe the information exchanged between your application, the generation pipeline, and underlying AI models.
AgentInfo: Describing the AI Participant
The AgentInfo interface identifies the generative model or service processing your request.
export interface AgentInfo {
id: string;
name: string;
role: string;
persona?: string;
}
Source: packages/@openmaic/generation/src/pipeline-types.ts (lines 7-13)
Use this to tag logs, route requests to specific model endpoints, or apply prompt templates tied to particular personas.
SceneGenerationContext: Maintaining Cross-Page Coherence
Multi-page presentations require narrative continuity. The SceneGenerationContext interface preserves speech patterns and structural awareness across generation calls.
export interface SceneGenerationContext {
pageIndex: number; // 1-based index of the current page
totalPages: number; // Total number of pages in the course
allTitles: string[]; // Ordered list of all page titles
previousSpeeches: string[]; // Speech texts from the preceding page only
}
Source: packages/@openmaic/generation/src/pipeline-types.ts (lines 17-23)
The previousSpeeches array intentionally includes only the immediate predecessor—this balances context window constraints with voice consistency.
GeneratedSlideData: Typed Slide Representation
The GeneratedSlideData interface defines the contract between AI generation and rendering systems. It enumerates supported visual elements and background specifications.
export interface GeneratedSlideData {
elements: Array<{
type: 'text' | 'image' | 'video' | 'shape' | 'chart' | 'latex' | 'line';
left: number;
top: number;
width: number;
height: number;
[key: string]: unknown;
}>;
background?: {
type: 'solid' | 'gradient';
color?: string;
gradient?: {
type: 'linear' | 'radial';
colors: Array<{ pos: number; color: string }>;
rotate: number;
};
};
remark?: string;
}
Source: packages/@openmaic/generation/src/pipeline-types.ts (lines 31-50)
The [key: string]: unknown index signature permits extensibility while the type discriminant enables exhaustive switch statements in consumers.
AICallFn: The LLM Abstraction Point
The AICallFn type in pipeline-types.ts decouples the pipeline from specific providers. Implement this interface to integrate OpenAI, Anthropic, local models, or mock services.
type AICallFn = (
systemPrompt: string,
userPrompt: string,
images?: Array<{ id: string; src: string }>
) => Promise<string>;
Source: packages/@openmaic/generation/src/pipeline-types.ts (lines 60-64)
The images parameter supports vision-capable models. The src field accepts URLs or base64 data URIs depending on your provider's requirements.
Practical Implementation Patterns
Implementing Custom Retry Logic with Generation Contracts
This example demonstrates withGenerationRetry with result validation and observable retry events.
import {
withGenerationRetry,
GenerationRetryOptions,
GenerationResult,
AICallFn,
} from '@openmaic/generation';
const unstableAICall: AICallFn = async (system, user) => {
// Simulate rate-limiting behavior
if (Math.random() < 0.5) {
throw Object.assign(new Error('Too Many Requests'), { status: 429 });
}
return JSON.stringify({
success: true,
data: { generatedText: user.toUpperCase() }
});
};
const retryConfig: GenerationRetryOptions<GenerationResult<{ generatedText: string }>> = {
label: 'Text transformation',
maxRetries: 4,
baseDelayMs: 500,
shouldRetryResult: (result) => !result.success || !result.data,
onRetry: ({ attempt, maxAttempts, reason, nextDelayMs }) => {
console.log(
`[${attempt}/${maxAttempts}] Retrying in ${nextDelayMs}ms: ${reason}`
);
},
};
async function transformWithRetry(input: string): Promise<string> {
const result = await withGenerationRetry(
async () => {
const raw = await unstableAICall(
'You are a text transformation service.',
input
);
return JSON.parse(raw) as GenerationResult<{ generatedText: string }>;
},
retryConfig
);
if (!result.success) throw new Error(result.error);
return result.data!.generatedText;
}
The shouldRetryResult predicate catches cases where the LLM returns valid JSON with an unsuccessful business logic status—essential for providers that don't surface all failures as HTTP errors.
Building a Complete Generation Pipeline
This implementation wires together all pipeline definitions into a cohesive workflow.
import {
AgentInfo,
SceneGenerationContext,
GeneratedSlideData,
AICallFn,
GenerationResult,
withGenerationRetry,
} from '@openmaic/generation';
// Configure the AI participant
const slideAgent: AgentInfo = {
id: 'gpt-4-turbo',
name: 'Slide Designer',
role: 'presentation_specialist',
persona: 'expert_educational_content_creator',
};
// Establish narrative context for page 3 of 5
const sceneContext: SceneGenerationContext = {
pageIndex: 3,
totalPages: 5,
allTitles: ['Introduction', 'Core Concepts', 'Implementation', 'Optimization', 'Conclusion'],
previousSpeeches: ['Welcome to our learning journey.', 'Let us examine the fundamentals.'],
};
// Provider-specific implementation of the contract
const anthropicCaller: AICallFn = async (system, user, images) => {
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.ANTHROPIC_KEY! },
body: JSON.stringify({
model: 'claude-3-opus-20240229',
system,
messages: [{
role: 'user',
content: images?.length
? [{ type: 'text', text: user }, ...images.map(img => ({
type: 'image' as const,
source: { type: 'url' as const, url: img.src }
}))]
: user
}],
max_tokens: 4096,
}),
});
if (!response.ok) throw new Error(`Anthropic error: ${response.status}`);
const data = await response.json();
return data.content[0].text;
};
// Retry-wrapped slide generation
async function generateSlideContent(topic: string): Promise<GeneratedSlideData> {
const result = await withGenerationRetry(
async () => {
const rawJSON = await anthropicCaller(
buildSlideSystemPrompt(slideAgent, sceneContext),
buildSlideUserPrompt(topic)
);
// Parse and validate against GenerationResult<GeneratedSlideData>
return validateSlideResponse(rawJSON);
},
{
label: `Slide generation: ${topic}`,
maxRetries: 3,
shouldRetryResult: (r: GenerationResult<GeneratedSlideData>) => !r.success,
}
);
if (!result.success) throw new Error(`Slide generation failed: ${result.error}`);
return result.data!;
}
function buildSlideSystemPrompt(agent: AgentInfo, ctx: SceneGenerationContext): string {
return `You are ${agent.name}, ${agent.persona}.
Current page ${ctx.pageIndex} of ${ctx.totalPages}.
Previous narrative: ${ctx.previousSpeeches.join(' → ')}`;
}
function buildSlideUserPrompt(topic: string): string {
return `Generate a slide for "${topic}" as valid JSON matching the GeneratedSlideData schema.`;
}
function validateSlideResponse(raw: string): GenerationResult<GeneratedSlideData> {
try {
const parsed = JSON.parse(raw);
// Schema validation would occur here
return { success: true, data: parsed };
} catch (e) {
return { success: false, error: `Invalid JSON: ${e.message}` };
}
}
The type system enforces that GeneratedSlideData flows through the pipeline, while GenerationResult handles both parsing failures and LLM-reported errors without exception throws.
Key Files and Module Structure
| File | Responsibility | Export Pattern |
|---|---|---|
src/pipeline-types.ts |
Pipeline data contracts (AgentInfo, SceneGenerationContext, GeneratedSlideData, GenerationResult, AICallFn) |
Named interfaces and types |
src/generation-retry.ts |
Retry contracts (GenerationRetryEvent, GenerationRetryOptions) and execution utilities |
withGenerationRetry, isRetryableGenerationError, isAbortError |
src/index.ts |
Public API surface | Re-exports all contracts and generators |
All paths are relative to packages/@openmaic/generation/.
Summary
-
Generation contracts (
GenerationRetryEvent,GenerationRetryOptions<T>,GenerationResult<T>) ingeneration-retry.tsprovide type-safe retry logic with exponential back-off, jitter, cancellation support, and result-based retry predicates. -
Pipeline definitions (
AgentInfo,SceneGenerationContext,GeneratedSlideData,AICallFn) inpipeline-types.tsestablish the data structures for AI orchestration, enabling cross-page coherence and provider abstraction. -
The
AICallFntype decouples your application from specific LLM implementations—swap providers without changing pipeline logic. -
withGenerationRetryhandles transient failures automatically while exposing observability throughGenerationRetryEventcallbacks. -
All contracts are re-exported from
src/index.tsfor convenient consumer imports.
Frequently Asked Questions
What is the difference between generation contracts and pipeline definitions in @openmaic/generation?
Generation contracts control execution semantics: how retries behave, what constitutes a failure, and how results are wrapped. They live in generation-retry.ts. Pipeline definitions describe the shape of data moving through your AI workflow—agent descriptions, scene context, and generated output structures. They live in pipeline-types.ts. Together they separate "how you call the AI" from "what you send and receive."
How does withGenerationRetry handle cancellation?
The function accepts an AbortSignal through GenerationRetryOptions.signal. When triggered, it stops the sleep delay and rejects with an abort error immediately. The isAbortError helper normalizes detection across browser, Node.js, and test environments—checking for DOMException, AbortError names, or signal-rejected promises depending on context.
Can I use GenerationResult for non-slide generation workflows?
Yes. GenerationResult<T> is generic and unconstrained. The OpenMAIC framework uses it for slide generation, but you can apply it to any operation needing consistent success/error discrimination. The interface intentionally avoids slide-specific fields to maximize reusability.
What happens when shouldRetryResult returns true?
The result predicate in GenerationRetryOptions runs after the operation completes successfully. If it returns true, the retry cycle continues exactly as for thrown errors—applying exponential back-off, invoking onRetry with a GenerationRetryEvent, and decrementing the remaining attempt budget. This catches "soft failures" like LLM responses with empty content field or schema validation failures.
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 →