Skip to main content

Image Generation

Generate images from text prompts across 8 built-in providers with a single API.


Table of Contents​

  1. Overview
  2. generateImage() API
  3. Provider Reference
  4. Provider Options Passthrough
  5. Local Setup (A1111 and ComfyUI)
  6. Custom Image Provider
  7. Usage Tracking

Overview​

generateImage() is provider-agnostic: call it the same way whichever backend serves it, and switch providers by changing provider.

Built-in providers (listImageProviderFactories() lists them):

ProviderIDKey Env VarDefault model
ReplicatereplicateREPLICATE_API_TOKENblack-forest-labs/flux-1.1-pro
falfalFAL_API_KEYfal-ai/flux/dev
Black Forest LabsbflBFL_API_KEYflux-pro-1.1
OpenAIopenaiOPENAI_API_KEYgpt-image-2.5-flare
Stability AIstabilitySTABILITY_API_KEYstable-diffusion-xl-1024-v1-0
OpenRouteropenrouterOPENROUTER_API_KEYnone: pass model
Stable Diffusion Localstable-diffusion-localSTABLE_DIFFUSION_LOCAL_BASE_URLv1-5-pruned-emaonly
MiniMaxminimaxMINIMAX_API_KEYimage-01

Ollama serves no image generation: provider: 'ollama' throws, since no image provider is registered under that id.

Provider choice and fallback. With provider (or a provider:model string in model), that provider serves the call. With neither, generateImage() takes the image model of a global default set with setDefaultProvider(), then the first provider whose key is in the environment, in the table's order (providerPreferences reorders or filters that chain). An empty or whitespace-only STABLE_DIFFUSION_LOCAL_BASE_URL is unset there, as the call reads it, so the local server is left out of the chain and the next provider serves the call. Either way, the other providers with keys in the environment become fallbacks: when the chosen provider fails, the next one is tried with its default model.

Mature content: pass policyTier: 'mature' or 'private-adult' and generateImage reroutes through the uncensored catalog (Replicate community models, face-consistency aware) and turns off Replicate's safety checker. See UNCENSORED_CONTENT.md for the full API and catalog.


generateImage() API​

import { generateImage } from '@framers/agentos';

const result = await generateImage({
// Required
prompt: 'A futuristic city at sunset with flying cars and neon lights, photorealistic.',

// Optional
provider: 'openai',
model: 'gpt-image-2.5-flare', // provider default if omitted
size: '1024x1024', // or aspectRatio: '16:9'
n: 1,
outputFormat: 'png', // 'png' | 'jpeg' | 'webp'
quality: 'high', // provider-specific quality tier
seed: 42, // providers with seeds
negativePrompt: 'blurry, text, watermark', // providers with negative prompts

// Provider-specific options (see per-provider sections)
providerOptions: {},
});

// Result shape
console.log(result.images[0].url ?? result.images[0].dataUrl); // URL or data URL, as the provider returns it
console.log(result.images[0].mimeType); // 'image/png'
console.log(result.model); // model used
console.log(result.provider); // provider used
console.log(result.usage?.totalImages); // 1

Each image is a GeneratedImage: url, dataUrl, base64, mimeType, revisedPrompt and providerMetadata, each set when the provider returns it.

Common Parameters​

ParameterTypeDescription
promptstringText description of the image (required)
providerstringProvider ID; auto-detected when omitted
modelstringModel ID, or provider:model; the provider default if omitted
nnumberNumber of images
sizestringDimensions as WxH
aspectRatiostring'1:1', '16:9', '9:16', '4:3', etc.
qualitystringProvider-specific quality tier
backgroundstringBackground mode, where the provider supports it
outputFormatstring'png', 'jpeg', 'webp'
outputCompressionnumberCompression level for lossy formats
responseFormatstringWhether the provider returns URLs or base64
seednumberSeed for reproducible generation
negativePromptstringWhat to exclude
referenceImageUrlstringReference image for face consistency (asks the mature-tier router for 'face-consistency')
providerOptionsobjectOptions keyed by provider ID
providerPreferencesobjectReorders or filters the auto-detected provider chain
policyTier, capabilitiesMature-tier routing
apiKey, baseUrlstringCredentials and endpoint override
usageLedgerobjectRecords the call's usage (Usage Tracking)

Provider Reference​

OpenAI​

const image = await generateImage({
provider: 'openai',
model: 'gpt-image-2.5-flare',
prompt: 'A minimal logo for a tech startup, flat design, blue and white.',
size: '1024x1024',
providerOptions: {
openai: {
moderation: 'low', // 'low' | 'auto'
// extraBody: fields sent as they are
},
},
});

Default model: gpt-image-2.5-flare through generateImage(), OpenAI's model for everyday generation; edits, variations and style transfer use gpt-image-2.5-sunburst, its model for precise edits. Env var: OPENAI_API_KEY.

OpenAI retired dall-e-2 and dall-e-3 on 2026-05-12. It retires gpt-image-1 on 2026-10-23, and gpt-image-1.5, gpt-image-1-mini and chatgpt-image-latest on 2026-12-01 (deprecations). A call that names one of them fails from its date on: name a GPT Image 2.5 model, or leave model out.

quality takes low, medium, high and auto, and on the 2.5 models also xhigh and max. DALL·E 3's standard and hd are sent to a GPT Image model as medium and high, and its style option is left out, since the GPT Image models do not take it: put the style in the prompt. On the GPT Image 2 and 2.5 models size takes any WIDTHxHEIGHT whose sides are divisible by 16, with an aspect ratio between 1:3 and 3:1, up to 3840x2160.


Stability AI​

const image = await generateImage({
provider: 'stability',
model: 'stable-image-core', // or 'stable-image-ultra', 'sd3-medium', 'sd3.5-large', ...
prompt: 'An art deco travel poster for a moon colony, vintage style.',
negativePrompt: 'text, watermark, low quality',
aspectRatio: '1:1',
providerOptions: {
stability: {
stylePreset: 'illustration',
cfgScale: 8, // classifier-free guidance scale
steps: 30,
},
},
});

The model picks the endpoint: core/stable-image-core and any id the provider does not know go to stable-image/generate/core, ultra/stable-image-ultra to generate/ultra, and the SD3 family (sd3, sd3-medium, sd3-large, sd3-large-turbo, sd3.5-medium, sd3.5-large, sd3.5-large-turbo) to generate/sd3; providerOptions.stability.engine overrides the model for that choice. The default model, stable-diffusion-xl-1024-v1-0, is not in that table, so it is served by the core endpoint.

Env var: STABILITY_API_KEY. Options: engine, negativePrompt, seed, stylePreset, cfgScale, steps, samples, strength, aspectRatio, outputFormat, extraFields.


Replicate​

const image = await generateImage({
provider: 'replicate',
model: 'black-forest-labs/flux-1.1-pro',
prompt: 'A product photo of a titanium watch on polished black stone.',
aspectRatio: '1:1',
providerOptions: {
replicate: {
outputQuality: 90,
outputFormat: 'webp',
input: {
safety_tolerance: 2, // model inputs sent as they are
},
},
},
});

Default model: black-forest-labs/flux-1.1-pro. Env var: REPLICATE_API_TOKEN. Options: wait, webhook, webhookEventsFilter, seed, negativePrompt, numOutputs, aspectRatio, outputFormat, outputQuality, disableSafetyChecker, goFast, megapixels, input, extraBody, referenceImageUrl, controlImage, controlType (canny, depth or pose).


OpenRouter​

OpenRouter generates images through its chat completions endpoint with image output, so the call names a model that returns images; OpenRouter has no default image model here.

const image = await generateImage({
provider: 'openrouter',
model: process.env.OPENROUTER_IMAGE_MODEL!, // an OpenRouter model with image output
prompt: 'Abstract geometric art, primary colors, Mondrian style.',
providerOptions: {
openrouter: {
// imageConfig: merged into the request's image_config
// provider, transforms, extraBody: OpenRouter request fields, sent as they are
},
},
});

Env var: OPENROUTER_API_KEY. Options: imageConfig, provider, transforms, extraBody.


Local Stable Diffusion​

Run Stable Diffusion locally against an Automatic1111 (A1111) or ComfyUI server.

const image = await generateImage({
provider: 'stable-diffusion-local',
model: 'v1-5-pruned-emaonly', // model checkpoint name
prompt: 'A brutalist house in dense fog, dramatic lighting.',
negativePrompt: 'blurry, low quality, text',
seed: 1234,
baseUrl: 'http://localhost:7860', // or set STABLE_DIFFUSION_LOCAL_BASE_URL
providerOptions: {
'stable-diffusion-local': {
width: 512,
height: 512,
steps: 25,
sampler: 'DPM++ 2M Karras',
cfgScale: 7,
hrFix: true,
denoisingStrength: 0.45,
},
},
});

Default model: v1-5-pruned-emaonly. Env var: STABLE_DIFFUSION_LOCAL_BASE_URL (required: without it or baseUrl the call throws). Options: steps, cfgScale, seed, sampler, negativePrompt, width, height, batchSize, controlnet, loras, hrFix, denoisingStrength.


BFL, fal and MiniMax​

  • Black Forest Labs (bfl, BFL_API_KEY): FLUX models such as flux-pro-1.1 (default), flux-pro-1.1-ultra and flux-dev.
  • fal (fal, FAL_API_KEY): default fal-ai/flux/dev.
  • MiniMax (minimax, MINIMAX_API_KEY): default image-01; options promptOptimizer, width, height. See MiniMax images.

Provider Options Passthrough​

providerOptions is keyed by provider ID, and each provider reads only its own key, so options for several providers can sit side by side:

const image = await generateImage({
provider: 'stability',
model: 'stable-image-core',
prompt: '...',
providerOptions: {
// Only the 'stability' key is read here
stability: { stylePreset: 'photographic' },
openai: { style: 'vivid' },
replicate: { outputQuality: 80 },
},
});

That keeps provider-specific settings in place when a call falls back to another provider, or when you switch with the provider field alone.


Local Setup (A1111 and ComfyUI)​

Automatic1111 WebUI​

# Clone and install
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui
cd stable-diffusion-webui

# Download a model checkpoint (example: SD 1.5)
mkdir -p models/Stable-diffusion
wget -O models/Stable-diffusion/v1-5-pruned-emaonly.safetensors \
https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors

# Launch with API enabled
./webui.sh --api --listen

# Server starts at http://localhost:7860

Set the env var:

export STABLE_DIFFUSION_LOCAL_BASE_URL=http://localhost:7860

ComfyUI​

git clone https://github.com/comfyanonymous/ComfyUI
cd ComfyUI
pip install -r requirements.txt

# Place model checkpoints in models/checkpoints/
python main.py --port 7860

export STABLE_DIFFUSION_LOCAL_BASE_URL=http://localhost:7860

The provider detects the backend when it initializes: it probes A1111's model list, then ComfyUI's /system_stats. On ComfyUI it builds a minimal text-to-image workflow and submits it to /prompt; image edits and upscales need an A1111 server (Image Editing).


Custom Image Provider​

Register any image backend not covered by the built-in set:

import {
generateImage,
registerImageProviderFactory,
type IImageProvider,
type ImageGenerationRequest,
type ImageGenerationResult,
} from '@framers/agentos';

// myImageAPI stands for your image service's client.
declare const myImageAPI: { generate(input: { prompt: string; model?: string }): Promise<{ imageUrl: string }> };

class MyImageProvider implements IImageProvider {
readonly providerId = 'my-provider';
isInitialized = false;
defaultModelId = 'my-default-model';

async initialize(config: Record<string, unknown>): Promise<void> {
if (typeof config.defaultModelId === 'string') this.defaultModelId = config.defaultModelId;
this.isInitialized = true;
}

async generateImage(request: ImageGenerationRequest): Promise<ImageGenerationResult> {
const response = await myImageAPI.generate({
prompt: request.prompt,
model: request.modelId,
});

return {
created: Math.floor(Date.now() / 1000),
modelId: request.modelId ?? this.defaultModelId,
providerId: this.providerId,
images: [{ url: response.imageUrl, mimeType: 'image/png' }],
usage: { totalImages: 1 },
};
}
}

// Register the factory
registerImageProviderFactory('my-provider', () => new MyImageProvider());

// Use it: a custom provider has no entry in the default-model table, so pass the model
const image = await generateImage({
provider: 'my-provider',
model: 'my-default-model',
prompt: 'A product photo on white background.',
});

Usage Tracking​

usageLedger records each call's tokens and cost to the usage ledger; getRecordedAgentOSUsage() reads them back as totals:

import { generateImage, getRecordedAgentOSUsage } from '@framers/agentos';

await generateImage({
provider: 'openai',
prompt: 'A banner image for our launch.',
usageLedger: {
enabled: true,
sessionId: 'launch-campaign',
},
});

const usage = await getRecordedAgentOSUsage({
enabled: true,
sessionId: 'launch-campaign',
});

console.log(usage.calls); // 1
console.log(usage.costUSD); // the cost the provider reported, 0 when it reports none

The ledger records promptTokens, completionTokens, totalTokens and costUSD; the image count is on the result's usage.totalImages.