Files
open-multi-agent/docs
Jack Chen 01b63594fb feat(core): add Black Forest Labs image adapter (#614)
* feat(core): add Black Forest Labs image adapter

Black Forest Labs (FLUX) generates asynchronously: a submit returns a polling_url, the task is polled until ready, and the image is downloaded from a result URL. BlackForestLabsImageAdapter runs that flow with fetch, sends input images as input_image, input_image_2, and so on, and maps a WIDTHxHEIGHT size to width and height.

A moderated task shows up only in the poll status. The adapter reports Request Moderated and Content Moderated as a non-retryable content_policy failure as soon as it sees them, so runImage moves to the next model instead of polling until the attempt deadline and retrying. Failed and lost tasks are retryable.

The API key goes only to the API origin and HTTPS hosts under bfl.ai, keyed requests refuse redirects, and the pre-signed image download carries no key. providerOptions follows the same rule as the other adapters: fields that carry the prompt or input images are rejected at construction, and width and height are defaults that request.size overrides.

Follow-up to #613.

* fix(core): report detected image media type and the BFL dimensions sent

The built-in image adapters declared a fixed media type, which was wrong whenever providerOptions chose another output format, for example output_format png on Black Forest Labs or jpeg on OpenAI. All four now report the type read from the returned bytes and fall back to the old default only when the bytes are not a recognized image.

The Black Forest Labs attempt parameters kept default width and height from providerOptions even when request.size replaced them in the request body. They now record the same width and height the request sent.

* fix(core): keep a submitted BFL task through transient poll failures

A transient failure while polling or downloading, such as a 429, a 5xx, or a network error, escaped the adapter as retryable, so runImage retried generate() and submitted another billable task while the first kept running. The Black Forest Labs adapter now retries those failures in place until the attempt deadline, honoring Retry-After. Non-retryable errors still throw at once, and so does a 4xx on the download, since an expired delivery link cannot recover without a new task.

The adapter contract now says what it meant: an adapter must not resubmit a generation internally, while retrying a status poll or download of an already-submitted task is allowed.

* fix(core): type rate-limited and timed-out image downloads

downloadImage turned every non-2xx status into api_error, so a 429 or 408 on a pre-signed result URL did not count as transient for the Black Forest Labs in-place retry, escaped to runImage, and resubmitted a billable task. A download 429 is now rate_limit with Retry-After and a 408 is timeout, so both are retried in place against the existing task.

* fix(core): scope BFL credentials and keep a submitted task final at the deadline

The Black Forest Labs adapter sent its key to any HTTPS host under bfl.ai. Behind a proxy that key belongs to the proxy, and a forwarded BFL polling URL would receive it. Sibling BFL hosts now qualify only when baseURL is itself a BFL host; otherwise the key goes to the configured origin alone.

A transient outage that lasted past the attempt deadline still led runImage to retry generate() and submit another billable task. Once a task is submitted, the adapter now reports a deadline as a non-retryable timeout, and runImage keeps a non-retryable adapter verdict at the deadline instead of replacing it with a retryable timeout, so the chain moves to the next model. A deadline before the submit completes stays retryable.

* fix(core): tell deadlines from cancellation and end BFL turns on permanent download failures

runImage now aborts an attempt's signal with a DOMException named TimeoutError when the deadline fires, the same convention as AbortSignal.timeout(), and with the caller's own reason on cancellation. The Black Forest Labs adapter turns only a deadline after the submit into a final timeout; a caller that cancels a direct generate() call gets its own abort reason back.

A permanent failure downloading a submitted task, such as an expired or missing result link, was still retryable, so runImage submitted and paid for another task. Once the task exists, a download failure that is not transient now ends that model's turn as non-retryable.

* fix(core): end a submitted BFL task's turn on any failure and keep its cost

Several failures after a Black Forest Labs task was submitted were still retryable: a Ready status without result.sample, an Error or Failed task, and a lost task. runImage retried them by submitting and paying for another task. Every failure after the submit now ends that model's turn as non-retryable.

The adapter's in-place retry of transient poll and download failures slept through any Retry-After, so a header above runImage's maxRetryAfterMs stalled the attempt until its deadline. runImage now passes its cap to adapters as ImageCallOptions.maxRetryAfterMs, and a longer wait ends the turn at once.

A failure after the submit also dropped the task ID and the cost BFL reported, because adapter params were only returned on success. ImageModelError now carries optional params, runImage copies them into the failed attempt record, and the BFL adapter attaches the task and its cost to every post-submit failure.

* fix(core): close the last BFL paths that resubmit a created task

Two failures after a successful Black Forest Labs submit still reached runImage as retryable and led it to submit and pay for another task: a submit response without polling_url, and a result download that answers 200 with bytes that are not an image. Both now end that model's turn as non-retryable invalid_output and carry the task ID and cost into the failed attempt record. The adapter checks the downloaded bytes itself instead of leaving it to runImage, whose retry of an invalid output would resubmit.
2026-09-24 03:59:11 +08:00
..

Documentation

Every page under docs/, grouped by what you are trying to do. Each line says which question the page answers, so you can pick one without opening five.

Start here

Page What it answers
Core package guide What are the three execution modes, and how do I get a team running?
Examples Which runnable script is closest to what I am building?
Glossary What does OMA mean by coordinator, task, run, span, gate, or budget?
Production checklist Which defaults are deliberately permissive, and what must I decide before going live?

Configure models and tools

Page What it answers
Providers Which providers are built in, what credentials do they need, and how do I point one at a local or OpenAI-compatible endpoint?
LLM egress policy Which network requests can OMA restrict before an adapter opens them, and which are outside that boundary?
Self-hosting and data residency What does the framework run, reach, and persist when it runs on my own infrastructure?
Tool configuration How are built-in, custom, and delegation tools granted, filtered, and gated per call?
Sandbox and shell execution Where can a filesystem tool reach, and where does a granted bash command actually run?
MCP tools What does connectMCPTools() do, and which OMA controls extend into the MCP child process?
Image generation and editing How do I generate or edit an image across a fallback chain of image models, and what does each attempt record?
Structured agent input How do I pass caller-owned conversation history, images, or files instead of a single prompt string?
External agents How do process and ACP backends put coding CLIs on the same task DAG, and what stops applying to them?

Control orchestration

Page What it answers
Coordinator What does the planning agent decide, what does it see, and how do I configure or replace it?
Task scheduling and dispatch In what order do tasks run, how do dependency payloads reach them, and what happens on retry, approval, or abort?
Execution routing Should an automatic runTeam() call use one agent or a coordinator-built team plan?
Model routing How do I send planning and leaf work to different models without changing the team?
Consensus How do judge agents verify an answer, and what does a quorum cost against the budget?
Plan preview and replay How do I freeze a coordinator plan as reviewable data and replay it without planning again?
Adaptive recovery When may a run revise the not-yet-executed part of its own graph?
Durable approval gates How does a run suspend at an approval boundary and resume from the same reviewed content after a restart?
Hooks and callbacks Which function-typed field fires when, in which run mode, and what can its return value change?
Shared memory How do agents read each other's findings, and which store backs that?
Streaming Which APIs return incremental output, what does each StreamEvent carry, and which paths never stream?
Budgets and limits Which ceilings bound a run, where are they checked, and what happens when one trips?

Operate

Page What it answers
Observability What do progress events, trace spans, and stores record, and what does telemetry cost?
Run Viewer How do I render one finished run as a self-contained page with a task DAG and span waterfall?
Run event journal What exactly did each agent see at the moment it was asked, and can that be verified offline?
Checkpoint and resume How does an interrupted run resume without repeating completed work?
Run store and execution leases Which worker owns a run, and what stops two of them from advancing it at once?
Context management How does a long conversation shrink as it grows, and what happens to reasoning blocks?
Evaluation How do EvalSets, scorers, and stores measure quality without changing the business result?
Evaluation in CI How do I turn an EvalSet into a pass/fail signal a CI job can act on?
Routing evaluation Which frozen EvalSets guard routing decisions, and what would let a routing regression through?
Migrating to Observability v2 How do I move an onTrace integration to the v2 path one layer at a time?
Errors Which error class is this, who raised it, and does a retry help?
CLI Which oma subcommands exist, what JSON do they print, and which exit codes do they use?

Partners

Page What it answers
Featured Partner program What does a featured placement in the project README include, and who qualifies?
Atlas Cloud setup guide How do I configure the Atlas Cloud provider sponsor's endpoint and credentials?
Atlas Cloud 接入指南 Chinese translation of the Atlas Cloud setup guide.

Internal records

internal/ holds engineering records kept for auditability, such as benchmark snapshots and release-readiness reviews. They describe a specific point in time on specific hardware and are not user documentation; nothing there is a supported contract.