@memberjunction/ai-blackforestlabs
MemberJunction AI provider for Black Forest Labs’ FLUX image generation models. This package implements the BaseImageGenerator interface to provide text-to-image generation, image editing (inpainting/outpainting), and image variations through BFL’s async task-based API.
Architecture
Section titled “Architecture”graph TD
A["FLUXImageGenerator<br/>(Provider)"] -->|extends| B["BaseImageGenerator<br/>(@memberjunction/ai)"]
A -->|submits tasks| C["BFL REST API"]
A -->|polls| D["Result Endpoint"]
D -->|returns| E["Image URL"]
A -->|downloads| F["Generated Image"]
B -->|registered via| G["@RegisterClass"]
style A fill:#7c5295,stroke:#563a6b,color:#fff
style B fill:#2d6a9f,stroke:#1a4971,color:#fff
style C fill:#2d8659,stroke:#1a5c3a,color:#fff
style D fill:#2d8659,stroke:#1a5c3a,color:#fff
style E fill:#b8762f,stroke:#8a5722,color:#fff
style F fill:#2d6a9f,stroke:#1a4971,color:#fff
style G fill:#b8762f,stroke:#8a5722,color:#fff
Async Task Flow
Section titled “Async Task Flow”sequenceDiagram
participant App as Application
participant FLUX as FLUXImageGenerator
participant BFL as BFL API
App->>FLUX: GenerateImage(params)
FLUX->>BFL: POST /flux-2-pro (submit task)
BFL-->>FLUX: { id: "task-123", polling_url: "https://api.bfl.ai/v1/get_result?id=task-123", cost: 4 }
loop Poll polling_url until ready
FLUX->>BFL: GET polling_url (or /get_result?id=task-123)
BFL-->>FLUX: { status: "Pending" }
end
BFL-->>FLUX: { status: "Ready", cost: 4, result: { sample: "url" } }
FLUX->>BFL: Download image from signed URL
BFL-->>FLUX: Image data
FLUX-->>App: ImageGenerationResult (with usage.cost in USD)
Features
Section titled “Features”- Text-to-Image Generation: Generate photorealistic images using FLUX.2 Pro, FLUX 1.1 Pro, and other FLUX models
- Image Editing: Inpainting and outpainting via FLUX Fill model with mask support
- Image Variations: Create variations using FLUX Kontext Pro for image-to-image generation
- Async Task Polling: Configurable polling with adjustable interval and timeout
- Multiple Models: Support for FLUX.2 Pro (32B), FLUX 1.1 Pro, FLUX Dev, and FLUX Ultra
- Seed Reproducibility: Deterministic generation with seed parameter support
- Resolution Control: Custom width/height and aspect ratio settings up to 4MP
Installation
Section titled “Installation”npm install @memberjunction/ai-blackforestlabsimport { FLUXImageGenerator } from '@memberjunction/ai-blackforestlabs';
const generator = new FLUXImageGenerator('your-bfl-api-key');
const result = await generator.GenerateImage({ prompt: 'A serene mountain landscape at sunset', model: 'flux-2-pro', size: '1536x1024'});
if (result.success) { const image = result.images[0]; console.log('Image generated, base64 length:', image.base64.length);}Configure Polling
Section titled “Configure Polling”generator.setPollingConfig({ maxWaitTime: 180000, // 3 minutes pollInterval: 3000 // 3 seconds});Supported Models
Section titled “Supported Models”| Model | ID | Modality | Description |
|---|---|---|---|
| FLUX 3 Image | flux-3-image | Image | Up to 4K resolution, 16 aspect ratios, and up to 10 reference images |
| FLUX 3 Video | flux-3-video | Video | Text-to-video and image-to-video (5–20s), continuation via v2v (5–15s); API supports hd..uhd (driver maps Quality up to qhd) with optional native audio; driver refuses VideoToVideo and measures MP4 bytes for C2PA |
| FLUX.2 Pro | flux-2-pro | Image | 32B parameter, photorealistic 4MP images |
| FLUX 1.1 Pro | flux-1.1-pro | Image | Production-ready, high-volume workloads |
| FLUX.1 Dev | flux-dev | Image | Open-weights 12B for development |
| FLUX Ultra | flux-ultra | Image | Ultra-high quality |
Class Registration
Section titled “Class Registration”- Image generator: registered as
FLUXImageGeneratorvia@RegisterClass(BaseImageGenerator, 'FLUXImageGenerator'). - Video generator: registered as
FLUX3VideoGeneratorvia@RegisterClass(BaseVideoGenerator, 'FLUX3VideoGenerator').
Verified against BFL docs
Section titled “Verified against BFL docs”- Verification date:
2026-10-16 - Documentation URLs read:
https://docs.bfl.ai/flux_3/flux3_overview(flux_3/flux3_overview.md)https://docs.bfl.ai/flux_3/flux3_video(flux_3/flux3_video.md)https://docs.bfl.ai/flux_3/flux3_image(flux_3/flux3_image.md)https://docs.bfl.ai/flux_3/flux3_image_generate(flux_3/flux3_image_generate.md)https://docs.bfl.ai/flux_tools/flux_video_edit(flux_tools/flux_video_edit.md)https://docs.bfl.ai/cookbook/video_quickstart(cookbook/video_quickstart.md)https://docs.bfl.ai/cookbook/video_edit_recast_continue(cookbook/video_edit_recast_continue.md)https://docs.bfl.ai/api_integration/integration_guidelines(api_integration/integration_guidelines.md)https://docs.bfl.ai/api-reference/utility/generate-a-video-with-flux-3(api-reference/utility/generate-a-video-with-flux-3.md)https://docs.bfl.ai/api-reference/utility/generate-an-image-with-flux-3(api-reference/utility/generate-an-image-with-flux-3.md)https://docs.bfl.ai/api-reference/utility/get-result(api-reference/utility/get-result.md)https://docs.bfl.ai/flux_2/flux2_text_to_image(flux_2/flux2_text_to_image.md)https://bfl.ai/pricing(1 credit = $0.01 USD)
- Endpoints & Authentication:
- Base URLs:
https://api.bfl.ai/v1(global),https://api.eu.bfl.ai/v1,https://api.us.bfl.ai/v1 - Auth header on submit and poll:
x-key: <BFL_API_KEY>(never sent when downloading signed delivery URLs ondelivery.*.bfl.ai) - FLUX 3 Video submit:
POST /v1/flux-3-video - FLUX 3 Image submit:
POST /v1/flux-3-image - FLUX.2 Pro submit:
POST /v1/flux-2-pro - Result polling:
GET <polling_url>from submit response (validated to behttps:and hostname ending in.bfl.ai), or fallbackGET /v1/get_result?id=<id>whenpolling_urlis omitted
- Base URLs:
- FLUX 3 Video (
Flux3VideoInputsBody,additionalProperties: false) parameters & limits:mode:'t2v' | 'i2v' | 'v2v'(required;'v2v'on/v1/flux-3-videois video continuation viastart_video, whereas video editing is a separate endpoint/v1/flux-tools/video-edit-v1;FLUX3VideoGeneratorrefusesMode: 'VideoToVideo')prompt: string (required)resolution:'hd' | 'fhd' | 'qhd' | 'uhd'(API schema default'hd'; driver mapsQuality:'draft' -> 'hd' + draft: true,'standard' -> 'fhd','high' -> 'qhd')aspect_ratio:'auto' | '21:9' | '2:1' | '16:9' | '4:3' | '1:1' | '3:4' | '9:16' | '9:21'(API schema default'auto'; driver requires one of the 8 fixed ratios so pixel dimensions are known;'4:5'is not supported)duration:'auto'or integer5..20seconds for't2v'and'i2v';5..15seconds for'v2v'(API schema default'auto'; driver requires an integer in5..20for't2v'/'i2v'or5..15for'v2v')generate_audio: boolean (BFL API defaults totrue;FLUX3VideoGeneratoralways sendsgenerate_audio: params.WantAudio === trueso omittedWantAudionever bills for audio)draft: boolean (optional; set totruefor'draft'quality tier)keyframes:Array<[number, string]> | string[](1..10items; required in'i2v'mode; timestamped[seconds, image]pairs with0 <= seconds <= durationor plain base64/HTTPS URL strings)start_video: string ('v2v'continuation mode only; base64 or HTTPS URL;mp4; ≤50 MB; ≤15s percookbook/video_edit_recast_continue.md)safety_tolerance: integer0..7(default2)user,version: optional strings in thet2v/i2v/v2vinput schemas (webhook_urlandwebhook_secretare not in thet2v/i2v/v2vinput schemas)- Does not accept
seed,fps,width, orheight.
- FLUX 3 Video 8×4 Dimension Lookup Table (
FLUX3_VIDEO_DIMENSIONS,width × height):
| Aspect Ratio | hd (720p) | fhd (1080p) | qhd (1440p) | uhd (4K) |
|---|---|---|---|---|
21:9 | 1440×608 | 2176×928 | 2912×1248 | 4352×1856 |
2:1 | 1344×672 | 2016×1024 | 2720×1376 | 4032×2048 |
16:9 | 1280×704 | 1920×1088 | 2560×1440 | 3840×2176 |
4:3 | 1088×800 | 1632×1216 | 2176×1632 | 3264×2432 |
1:1 | 960×960 | 1440×1440 | 1920×1920 | 2880×2880 |
3:4 | 800×1088 | 1216×1632 | 1632×2176 | 2432×3264 |
9:16 | 704×1280 | 1088×1920 | 1440×2560 | 2176×3840 |
9:21 | 608×1440 | 928×2176 | 1248×2912 | 1856×4352 |
- FLUX 3 Image (
Flux3ImageInputs,additionalProperties: false) parameters & limits:prompt: string (required)images: string[] | null (optional; up to10reference images)aspect_ratio:'auto' | '21:9' | '2:1' | '16:9' | '3:2' | '7:5' | '4:3' | '5:4' | '1:1' | '4:5' | '3:4' | '5:7' | '2:3' | '9:16' | '1:2' | '9:21'(API schema default'auto'; driver omitsaspect_ratiowhen no dimensions or aspect ratio are specified so BFL uses'auto')resolution:'768sq' | '1k' | '1.5k' | '2k' | '4k'(default'1k')grounding: boolean (BFL API defaults totrue;FLUXImageGeneratoralways setsgrounding: falseso image generation never makes unsolicited web searches)safety_tolerance: integer0..7(default2)- Does not accept
width,height,seed, oroutput_format.
- Polling Statuses (
StatusResponse— 9 values):- Active:
'Pending','Reasoning','Generating' - Success:
'Ready'(result.samplecontains the signed delivery URL) - Terminal failures:
'Error','Request Moderated','Content Moderated','No Card Generated'(surfacesdetails['Moderation Reasons']when present),'Task not found'
- Active:
- Pricing Status (
bfl.ai/pricing,1 credit = $0.01 USD):- Submit responses include initial
cost(credits) and/v1/get_resultincludes settledcost(credits) on'Ready'. Both drivers convert(pollResult.cost ?? submitResult.cost) / 100to USD onModelUsage. - Published FLUX 3 Video credits per output second on
bfl.ai/pricing:- Draft 720p (
hd):6credits/s ($0.06/s) - Standard
t2v/i2v: 720p (hd)15credits/s ($0.15/s), 1080p (fhd)24credits/s ($0.24/s), 1440p (qhd)28credits/s ($0.28/s), 2160p (uhd)41credits/s ($0.41/s) - Video-to-video (
v2vcontinuation): 720p (hd)21credits/s ($0.21/s), 1080p (fhd)27credits/s ($0.27/s), 1440p (qhd)31credits/s ($0.31/s), 2160p (uhd)45credits/s ($0.45/s)
- Draft 720p (
- Published FLUX 3 Image rates per image on
bfl.ai/pricing:768×768$0.041,1K$0.048,1.5K$0.07,2K$0.10,4K$0.607(reference images and prompts are not charged extra).
- Submit responses include initial
- Delivery URL Expiry Conflict:
- BFL’s documentation states three different expiry windows for signed
result.sampledelivery URLs: 10 minutes (integration guide), ~1 hour (FLUX 3 text-to-image and Video Edit pages), and ~2 hours (video quickstart and FLUX 3 overview). Both drivers download bytes immediately upon'Ready'and never persistresult.sample.
- BFL’s documentation states three different expiry windows for signed
- C2PA Provenance:
- BFL’s help center documents C2PA embedding for API images only (nothing in BFL’s docs states C2PA for video).
FLUX3VideoGeneratormeasures the downloaded MP4 bytes at runtime withHasC2PAManifest(bytes)(top-level ISO-BMFFuuidbox with extended typed8fec3d6-1b0e-483c-9297-5828877ec481, C2PA 2.2 §A.5.1.1) and setsresult.Provenance = { C2PA: HasC2PAManifest(bytes) }.
- BFL’s help center documents C2PA embedding for API images only (nothing in BFL’s docs states C2PA for video).
Opt-in Live Smoke Test (Run Only With Explicit Approval)
Section titled “Opt-in Live Smoke Test (Run Only With Explicit Approval)”Automated test suites in CI and local builds never make live network calls to BFL. To run a live end-to-end smoke check (only with explicit approval from Colin):
- Prerequisites:
metadata/prompts/.default-generative-video-prompt.json(VS-GEN-2) and the activeFLUX 3 Videomodel +Per Secondprice unit (VS-GEN-4) are synced to the target SQL Server database.- Standard MJ database environment variables (
DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD) andBFL_API_KEYare set. - Run from
packages/MJServer(after building@memberjunction/ai-blackforestlabsand@memberjunction/ai-prompts).
- Expected spend: 1 draft 5-second 720p (
hd) clip ≈ 30 credits ($0.30 USD) atbfl.ai/pricing’s draft rate of 6 credits/s.
cd packages/MJServernode --input-type=module -e 'import { writeFileSync } from "node:fs";import sql from "mssql";import { setupSQLServerClient, SQLServerProviderConfigData, UserCache } from "@memberjunction/sqlserver-dataprovider";import { RunView } from "@memberjunction/core";import { AIEngine } from "@memberjunction/aiengine";import { AIVideoRunner } from "@memberjunction/ai-prompts";import "../AI/Providers/BlackForestLabs/dist/index.js";
const pool = new sql.ConnectionPool({ server: process.env.DB_HOST ?? "localhost", port: Number(process.env.DB_PORT ?? 1433), database: process.env.DB_DATABASE, user: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, options: { encrypt: true, trustServerCertificate: true },});await pool.connect();await setupSQLServerClient(new SQLServerProviderConfigData(pool, process.env.MJ_CORE_SCHEMA ?? "__mj"));
const user = UserCache.Instance.Users[0];await AIEngine.Instance.Config(false, user);
const runner = new AIVideoRunner();const result = await runner.RunGenerateVideo({ Prompt: "A calm ocean wave rolling onto a sandy beach at sunrise", Mode: "TextToVideo", DurationSeconds: 5, AspectRatio: "16:9", Quality: "draft", WantAudio: false, ContextUser: user, CredentialScope: "RuntimeOnly", APIKeys: [{ driverClass: "FLUX3VideoGenerator", apiKey: process.env.BFL_API_KEY }],});
if (!result.Success || !result.Video?.Data?.length) { console.error("Smoke test failed:", result.ErrorMessage); process.exit(1);}
writeFileSync("/tmp/flux3-smoke.mp4", result.Video.Data);console.log("Saved /tmp/flux3-smoke.mp4", { bytes: result.Video.Data.length, width: result.Video.Width, height: result.Video.Height, durationSeconds: result.Video.DurationSeconds, c2pa: result.C2PA, promptRunId: result.PromptRunID,});
const rv = new RunView();const runRes = await rv.RunView({ EntityName: "MJ: AI Prompt Runs", ExtraFilter: `ID = '\''${result.PromptRunID}'\''`, Fields: ["ID", "Success", "Cost", "CostCurrency", "TokensUsedRollup"], ResultType: "simple",}, user);console.log("PromptRun row:", runRes.Results[0]);await pool.close();process.exit(0);'Then open /tmp/flux3-smoke.mp4 to verify playback and confirm PromptRun row has a non-null Cost (~0.30 USD).
Dependencies
Section titled “Dependencies”@memberjunction/ai- Core AI abstractions (BaseImageGenerator, BaseVideoGenerator, GeneratedImage)@memberjunction/global- Class registration