Feat(model support): ideogram4 support#9303
Conversation
Switches compel from PyPI 2.1.1 to invoke-ai/compel@main fork which supports transformers 5.x. Bumps transformers floor to 5.9.0. Removes the transformers>=5.1.0 uv override that was only needed to bypass compel 2.1.1's <5.0 constraint. NOTE: compel fork pulls notebook dep (full Jupyter stack); flag to maintainer for cleanup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s 5.x transformers 5.x no longer exposes rope_theta as a top-level attribute on Qwen3Config; the value is stored in the rope_parameters (and rope_scaling) dict instead. Read it from there with a getattr fallback so the inv_freq buffer is computed from the configured base (1e6 / 256) instead of raising AttributeError. Applies to both the safetensors and GGUF Qwen3 encoder paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…whoami huggingface_hub 1.x removed get_token_permission(). HFTokenHelper.get_status() now validates the token via whoami(), which returns user info for a valid token and raises HfHubHTTPError for an invalid one. Preserves the original three-way status: VALID on success, INVALID on HfHubHTTPError (e.g. 401), UNKNOWN on any other error (e.g. network failure). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
….9-compel-fork # Conflicts: # invokeai/app/api/routers/model_manager.py # invokeai/app/invocations/sd3_text_encoder.py # invokeai/backend/model_manager/metadata/fetch/huggingface.py # pyproject.toml # uv.lock
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The upstream merge left an unresolved conflict marker in _t5_encode and reintroduced T5TokenizerFast. Keep our v5 assertion (T5Tokenizer only) plus upstream's new t5_device logic, and drop the now-dead T5TokenizerFast monkeypatch in the test (the name no longer exists in the module). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- flux_text_encoder.py: drop unused typing.Union (F401) left by v5 import merge - huggingface.py: ruff format (wrap append(SimpleNamespace(...))) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
transformers 5.6 flattened CLIPTextModel (removed the self.text_model wrapper, hoisted embeddings/encoder/final_layer_norm to the top level). diffusers' single-file checkpoint loader (create_diffusers_clip_model_from_ldm) still assumes the nested layout, so loading SD1.5 .safetensors checkpoints fails on 5.6+ with 'CLIPTextModel object has no attribute text_model' and, once that read is shimmed, 'Cannot copy out of meta tensor' (weights never populate the flattened model). Pin to >=5.5,<5.6 (last pre-flattening release) which keeps both the single-file and from_pretrained paths working. The invoke-ai/compel fork accepts any 5.x. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore(deps): replace compel fork with official compel 2.4.0 compel 2.4.0 (released 2026-05-30) merges the transformers-5 support that the invoke-ai fork carried (both descend from upstream PR invoke-ai#129), plus the maintainer-reviewed padding rework and added diffusers/T5 smoke coverage. Switch from the git fork to the PyPI release. - pyproject: compel git+main -> compel>=2.4.0,<3 - uv.lock: compel 2.3.1 (git 8f404b45) -> 2.4.0 (pypi) - transformers stays 5.5.4 (satisfies compel >=5,<6 and our <5.6 pin) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
Vendor the Apache-2.0 Ideogram 4 reference model (DiT, FLUX2-style VAE, logit-normal flow-match scheduler, nf4/fp8 quant loading) into invokeai/backend/ideogram4/, plus InvokeAI glue (Qwen3-VL text encoding, packed-input build, dual-branch Euler denoise loop). Register the model: BaseModelType.Ideogram4, Main_Diffusers_Ideogram4_Config (detected via the Ideogram4Pipeline class name in model_index.json), and the Ideogram4DiffusersModel loader that loads both transformers as one Ideogram4TransformerPair submodel plus the Qwen3-VL encoder and VAE. Text-to-image only.
… loading End-to-end text-to-image backend for Ideogram 4, validated through the real session runner. Vendors the Apache-2.0 reference model (DiT, FLUX2-style VAE, logit-normal flow-match scheduler) into invokeai/backend/ideogram4/ with InvokeAI glue. Registers BaseModelType.Ideogram4, Main_Diffusers_Ideogram4_Config, and the Ideogram4DiffusersModel loader (two transformers as one Ideogram4TransformerPair; Qwen3-VL encoder + VAE). Both transformers and the encoder load via InvokeLinearNF4 so they work with the partial-load cache. Adds Ideogram4ConditioningInfo/Field/Output and the model_loader/text_encoder/denoise/l2i invocations. Text-to-image only.
Wires Ideogram 4 into the canvas/generate UI. buildIdeogram4Prompt assembles the structured JSON caption from the global prompt + Canvas Regional Guidance layers (each region → an obj element with a 0–1000 bbox + desc), with raw-JSON passthrough and a plain-text fallback when there are no regions. Adds buildIdeogram4Graph (text-to-image only, no negative prompt) and the enqueue switch. Structured captions use a static string node + a decoy positive-prompt node so the linear batch can't clobber the assembled JSON; plain text uses the real node so dynamic prompts/batching still work. Registers the 'ideogram-4' base (enums, color, names, model picker, grid size 16), a sampler-preset param (V4_QUALITY_48/V4_DEFAULT_20/V4_TURBO_12) replacing the steps/CFG controls, ParamIdeogram4SamplerPreset, and metadata recall. Regenerates schema.ts.
Advanced accordion now shows only Ideogram 4-relevant controls. Adds optional overrides of the sampler preset — steps, guidance scale (overrides the main gw, preserves the preset's polish tail), and schedule shift (mu) — plus a color palette editor that injects style_description.color_palette into the auto-built JSON caption (uppercase #RRGGBB, max 16, ignored for raw-JSON prompts). All are nullable (null = use preset), recallable from metadata, and the irrelevant controls (VAE, CLIP skip, CFG rescale, seamless, color compensation) are hidden for Ideogram 4. Backend denoise gains steps/guidance_scale/mu fields; schema.ts regenerated.
bitsandbytes has no macOS wheels and is excluded on darwin, but the module-level import broke test collection on macOS CI. Move the import into the two bnb-only functions and a TYPE_CHECKING block so the fp8 path imports without bitsandbytes installed.
bitsandbytes has no macOS wheels and is excluded on darwin, but the module-level import broke test collection on macOS CI. Move the import into the two bnb-only functions and a TYPE_CHECKING block so the fp8 path imports without bitsandbytes installed.
JPPhoto
left a comment
There was a problem hiding this comment.
Some findings from the latest group of changes:
-
invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:114: structured Ideogram prompts returnpositive_prompt_decoyas the graph builder'spositivePrompt, while the actualideogram4_text_encoder.promptinput is connected toideogram4_promptwhose value was assembled before batching atinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:57andinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:100;prepareLinearUIBatch()only injects dynamic prompt expansions into the returned node atinvokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts:74. Trigger: Ideogram 4 generation with a color palette, regional guidance, or raw JSON prompt, plus dynamic prompts or prompt batching. Consequence: the encoded prompt stays at the original static structured caption while metadatapositive_promptchanges through the decoy, so batched images can all use the same text conditioning. Test: build an Ideogram graph withideogram4ColorPalettenon-empty anddynamicPrompts.promptscontaining two distinct prompts, runprepareLinearUIBatch(), and assert the batch data updates the node connected toideogram4_text_encoder.promptor that each structured caption contains the expanded prompt. -
invokeai/backend/model_manager/load/model_loaders/ideogram4.py:178: the non-fp8 text encoder path callsmodel.load_state_dict(sd, strict=False, assign=True)and discards the returned missing and unexpected keys, unlike the fp8 helper which raises on unexpected keys and warns or raises on missing keys. Trigger: an nf4 or unquantized Ideogram text encoder checkpoint with a typo, extra component key, missing tied-weight exception other than the expected one, or mismatched architecture. Consequence: model import can appear successful with ignored checkpoint mismatch and then fail later during encoding or produce invalid conditioning. Test: monkeypatchAutoModel.from_config()with a tiny model and_load_local_state_dict()with one unexpected key and one non-tolerated missing key, then assert_load_text_encoder()raises or explicitly validates only the known tolerated missing keys. -
invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4SamplerPreset.tsx:11: the Ideogram sampler preset dropdown uses hardcoded English option labels (Quality (48 steps),Default (20 steps),Turbo (12 steps)) even though the component already usesuseTranslation()for the field label andinvokeai/frontend/web/public/locales/en.json:1635only adds the genericsamplerPresetlabel. Trigger: running the UI in any non-English locale. Consequence: newly added visible Ideogram controls remain partially untranslated. Test: renderParamIdeogram4SamplerPresetwith a non-English i18n resource that translates the three preset labels and assert the combobox options come from translation keys rather than literal English strings. -
invokeai/app/invocations/ideogram4_denoise.py:41:_effective_guidance_schedule()allowsnum_steps=1throughstepsvalidation atinvokeai/app/invocations/ideogram4_denoise.py:72, butpolish_count = min(num_steps, ...)makesmain_countzero despite the docstring promising at least one polish and one main step. Trigger: user sets the advanced Ideogram step override to1, optionally with a guidance override. Consequence: the generated schedule is only the polish weight, so the user-specified mainguidance_scaleis ignored and the denoise behavior contradicts the invocation contract. Test: unit test_effective_guidance_schedule(PRESETS["V4_QUALITY_48"].guidance_schedule, 48, 1, 12.0)and assert the invocation either rejects one-step overrides or returns behavior that reflects the documented guidance override.
…ntime caption) - Non-fp8 Ideogram 4 text-encoder load now validates the state dict: unexpected keys raise, missing keys warn (mirrors the fp8 helper) instead of silently accepting a partial load. - Guidance schedule: cap the polish tail at num_steps-1 so at least one main step always remains (the guidance_scale override was silently dropped at num_steps=1), and require steps >= 2 (backend field + frontend slider/marks). - Localize the Ideogram sampler-preset option labels via t() with the step count interpolated; add the three preset i18n keys. - Assemble the structured JSON caption at generation time in a new ideogram4_caption_builder node (Python port of buildIdeogram4Caption) instead of at graph-build time. The graph now wires the real prompt node -> caption builder -> text encoder and returns it as positivePrompt, so dynamic prompts / prompt batching vary the encoded caption (the decoy that dropped them is removed). The builder's output is wired to a new declared ideogram4_caption metadata field via an edge, so each batched image records its actual caption. Regenerates schema.ts for the new node + metadata field. Adds tests for caption assembly, the guidance schedule, and the graph wiring.
… filter - Emit a low-res progress preview each denoise step so the forming image is visible during generation, like the other denoise nodes. Ideogram uses a FLUX.2-style 32-channel VAE, so the packed latent is unpatchified/denormalized (get_latent_norm) and run through the FLUX.2 latent->RGB factors — no full VAE decode per step. The denoise loop now hands the callback the packed grid latent. - Document Ideogram 4's built-in content safety filter in models.mdx: it lives in the model weights (not Invoke's NSFW checker, can't be disabled from Invoke) and false-positives on benign prompts; structured JSON prompts trip it less.
…+ caption visibility
The main fix: Ideogram 4's built-in safety filter (baked into the model weights)
false-positives and returns an "Image blocked by safety filter" placeholder for
"degenerate" captions — empirically, an empty `compositional_deconstruction.elements`
list, or a single full-frame [0,0,1000,1000] element whose desc just repeats the
high_level_description. Our assembly produced empty elements whenever the user drew
no regions, so plain prompts were blocked.
- Caption assembly (build_ideogram4_caption):
- Always emit a structured JSON caption; never bare plain text (the filter
false-positives far more on plain text). Raw-JSON pastes still pass through.
- When there are no regions, synthesize one default element describing the whole
scene from the prompt with a *partial* (non-full-frame) bbox [100,100,900,900].
This never yields an empty/degenerate elements list. Verified end-to-end against
the model: the previously-blocked "golden retriever on a skateboard" now renders.
- Metadata: always wire the caption builder's output to the ideogram4_caption
metadata field, so the viewer's "Structured Caption" row shows the exact JSON that
was encoded (not just the raw prompt) for every generation.
- Denoise: emit a low-res progress preview each step (unpatchify + FLUX.2 latent->RGB
factors, since Ideogram uses a FLUX.2-style 32-channel VAE) so the forming image is
visible during generation, like the other denoise nodes.
- Docs: document the model's built-in safety filter in models.mdx (it's not Invoke's
NSFW checker, can't be disabled from Invoke, and false-positives).
Updates the caption/graph tests accordingly (also fixes latent tsc errors in the
graph-builder test's core_metadata / ideogram4_caption comparisons).
JPPhoto
left a comment
There was a problem hiding this comment.
While only the first is truly a merge blocker, I would like to see both addressed:
-
invokeai/frontend/web/src/features/controlLayers/store/types.ts:826:ideogram4Stepsstill accepts1, andIdeogram4Steps.parse()also recallsideogram4_steps: 1atinvokeai/frontend/web/src/features/metadata/parsing.tsx:942, but the backend invocation now requiressteps >= 2atinvokeai/app/invocations/ideogram4_denoise.py:85. A recalled image metadata value or rehydrated client state withideogram4_stepsset to1is dispatched without clamping atinvokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts:105and then passed directly into the graph atinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:95. Consequence: the UI can build/enqueue an Ideogram graph that violates the backend schema instead of rejecting or normalizing the stale value before enqueue. Test: recall Ideogram metadata or rehydrate params withideogram4_steps: 1, build an Ideogram graph, and assert the client rejects/clamps it before enqueue or that parsing refuses the value. -
invokeai/app/invocations/ideogram4_caption.py:16:Ideogram4Region.bboxis declared asOptional[list[int]]with no length or value constraints, while the description and model contract require exactly four normalized coordinates in the0..1000range.build_ideogram4_caption()forwards any provided bbox unchanged into the structured JSON atinvokeai/backend/ideogram4/caption.py:59, so a workflow/API caller can provide values like[0, 0, 1000],[0, 0, 1000, 1000, 7], or negative/out-of-range coordinates. Consequence: the new public caption-builder node can emit malformed structured prompts that the text encoder/model may misinterpret, ignore, or apply to the wrong region. Test: validate or executeIdeogram4CaptionBuilderInvocationwith short, long, and out-of-rangebboxarrays and assert validation fails instead of serializing them into the caption.
Address review on the Ideogram 4 PR: - The backend denoise node requires steps >= 2, but the client still accepted ideogram4_steps = 1 in three places, letting a recalled or rehydrated value build a graph that violates the backend schema. Tighten the zod schema to min(2) with `.catch(null)` (a stale/out-of-range value normalizes to null = use the preset instead of failing the whole persisted slice), normalize dispatched values through the schema in setIdeogram4Steps, and refuse an out-of-range value in the ideogram4_steps metadata recall parser. The slider was already min=2. - Ideogram4Region.bbox was an unconstrained Optional[list[int]], so a workflow/API caller could pass a wrong-length or out-of-range box that the caption builder serialized verbatim into the structured prompt. Add a field validator requiring exactly four coordinates, each in 0..1000. Add tests for both: the region bbox contract (valid/None accepted; short, long, negative, and >1000 rejected) and the ideogram4Steps normalization (valid kept, null kept, stale 1 normalized to null on both dispatch and rehydrate).
JPPhoto
left a comment
There was a problem hiding this comment.
A few more issues...
Blockers
-
invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts:73: the canvas enqueue path routes every Ideogram canvas job tobuildIdeogram4Graph(), butbuildIdeogram4Graph()assertsgenerationMode === 'txt2img'atinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:40. The compositor returnsimg2img,outpaint, orinpaintwhen visible raster/inpaint content exists atinvokeai/frontend/web/src/features/controlLayers/konva/CanvasCompositorModule.ts:711, whilegetRasterLayerWarnings()andgetInpaintMaskWarnings()still return no problems atinvokeai/frontend/web/src/features/controlLayers/store/validators.ts:241andinvokeai/frontend/web/src/features/controlLayers/store/validators.ts:252. Trigger: select an Ideogram 4 model on Canvas with an enabled raster layer or inpaint mask. Consequence: readiness can allow an unsupported canvas mode and enqueue only fails during graph build. Test: create canvas state with an Ideogram model plus an opaque raster layer and with an inpaint mask, then assert canvas readiness reports an unsupported-mode reason before enqueue calls the graph builder. -
invokeai/frontend/web/src/features/queue/store/readiness.ts:562: canvas readiness has model-specific 16/32-pixel bbox checks for Flux, Flux2, CogView4, and Qwen Image throughinvokeai/frontend/web/src/features/queue/store/readiness.ts:762, but no Ideogram 4 branch.getOriginalAndScaledSizesForTextToImage()uses raw bbox dimensions whenscaleMethod === 'none'atinvokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts:132,buildIdeogram4Graph()passes those dimensions intoideogram4_denoiseatinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts:116, and the backend requires both dimensions to be multiples of 16 atinvokeai/app/invocations/ideogram4_denoise.py:81. Trigger: Ideogram 4 on Canvas with bbox scaling disabled and a bbox like1025x1024. Consequence: readiness can allow a graph whose denoise node fails backend validation. Test: build canvas readiness for Ideogram 4 withbbox.scaleMethodset tononeand a non-16-multiple width or height, and assert it blocks with the same incompatible-bbox reason used for other 16-grid models. -
invokeai/frontend/web/src/features/controlLayers/store/validators.ts:50:getRegionalGuidanceWarnings()has no Ideogram 4 branch, butcollectIdeogram4PromptInputs()only readsregion.positivePromptand bbox atinvokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.ts:83. Trigger: an enabled Regional Guidance layer under Ideogram 4 with only a negative prompt, auto-negative, or reference image. Consequence: readiness can allow the layer with no warning, while the Ideogram graph silently drops those inputs and may omit the region entirely. Test: create Ideogram canvas state with regional guidance containing only a negative prompt and/or reference image, assert readiness reports unsupported ignored inputs, and assert graph construction does not silently discard a layer without a warning.
Follow-up PR
-
invokeai/app/invocations/ideogram4_caption.py:35:_validate_bbox()enforces length and0..1000range, but does not enforcey_min <= y_maxandx_min <= x_max;build_ideogram4_caption()forwards the bbox verbatim atinvokeai/backend/ideogram4/caption.py:59. Trigger: a custom workflow/API call supplies[900, 900, 100, 100]. Consequence: the caption builder emits an inverted region bbox that satisfies validation but violates the model prompt contract. Test: instantiateIdeogram4Region(prompt="x", bbox=[900, 900, 100, 100])and assert validation rejects it. -
invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx:66: the advanced accordion badge selector only distinguishes Flux and Flux2, so Ideogram 4 falls into the generic!isFlux2branch at line 77 and can show stale VAE, clip skip, CFG rescale, or seamless badges even though those controls are hidden for Ideogram at lines 116 and 122. Trigger: switch from an SD/SDXL model with VAE/clip skip/rescale/seamless settings to Ideogram 4. Consequence: the collapsed Advanced accordion advertises settings that no longer apply to the selected model. Test: renderAdvancedSettingsAccordionwith an Ideogram model and stale VAE/clipSkip/cfgRescale/seamless params, and assert only Ideogram-relevant badges appear or no stale badges appear. -
invokeai/frontend/web/src/services/api/schema.ts:14045:Ideogram4Region.bboxis generated asnumber[] | nullwith no length or range constraints, even though the backend validator atinvokeai/app/invocations/ideogram4_caption.py:24rejects anything other than exactly four coordinates in0..1000. Trigger: a workflow/API client generated from OpenAPI can construct[0, 0, 1000]or[0, 0, 1000, 1001]without schema/type feedback. Consequence: clients discover the contract only through runtime invocation validation instead of the public schema. Test: regenerate OpenAPI/types after changingbboxto schema-expressible constraints and assertopenapi.jsonincludesminItems,maxItems, and itemminimum/maximumforIdeogram4Region.bbox.
…ion inputs, constrain bbox Address the latest review on the Ideogram 4 PR: - Canvas readiness allowed unsupported generation modes: Ideogram 4 is txt2img-only (buildIdeogram4Graph asserts it), but a raster layer or inpaint mask makes the compositor pick img2img/outpaint/inpaint, failing only at graph build. Warn in getRasterLayerWarnings/getInpaintMaskWarnings for Ideogram 4 (these already flow into canvas readiness reasons), blocking enqueue up front. - Canvas readiness had no Ideogram 4 bbox check; the backend requires multiples of 16. Add the 16-grid check mirroring the other 16-grid models so an off-grid bbox (e.g. 1025x1024) is blocked instead of failing backend validation. - getRegionalGuidanceWarnings had no Ideogram 4 branch, so a region whose only input is a negative prompt, auto-negative, or reference image looked effective while the graph silently drops it. Warn those inputs are unsupported. - Ideogram4Region.bbox now also rejects inverted boxes (y_min <= y_max, x_min <= x_max). - The advanced-settings badge selector lumped Ideogram 4 into the generic branch, showing stale VAE/clip-skip/CFG-rescale/seamless badges for controls that are hidden for Ideogram. Exclude Ideogram 4 from that branch. - Model bbox as a constrained type (exactly 4 ints, each 0..1000) so the OpenAPI schema advertises minItems/maxItems and item minimum/maximum. Add readiness tests (bbox grid, raster/inpaint blocking, empty-layer allowance, regional-guidance negative/reference-image warnings) and bbox ordering-rejection tests.
JPPhoto
left a comment
There was a problem hiding this comment.
Another one I can't test so hopefully you have done so!
Only one minor change if you can do it (or put it in a follow-up), but I am approving this work anyway:
invokeai/backend/model_manager/load/model_loaders/ideogram4.py:168andinvokeai/backend/model_manager/load/model_loaders/ideogram4.py:179:_load_text_encoder()still allows missing text-encoder keys to proceed as warnings after building the model underaccelerate.init_empty_weights(). Any missing non-tied parameter can remain on the meta device, so a bad or mismatched Ideogram text encoder may appear to load and then fail later during device movement or encoding instead of being rejected at load time. Test: monkeypatchAutoModel.from_config()with a tiny meta-built model and_load_local_state_dict()with one missing non-tied parameter for both fp8 and non-fp8 paths, then assert_load_text_encoder()raises or filters only explicitly tolerated tied-weight keys and asserts no meta tensors remain.
…vice _load_text_encoder() builds the encoder under accelerate.init_empty_weights() and previously downgraded missing keys to a warning (both the fp8 path via load_fp8_state_dict(strict=False) and the non-fp8 path). A missing non-tied weight therefore stayed on the meta device, so a bad or mismatched encoder appeared to load and only failed later during device movement or encoding. Add _verify_encoder_fully_materialized(): call tie_weights() to materialize tied weights from their source, then hard-fail if any parameter or buffer remains on the meta device. Wire it into both the fp8 and non-fp8 (incl. bnb-nf4) paths and drop the missing-key warning — genuinely missing non-tied weights are now caught as leftover meta tensors, while tied weights are tolerated. This is a state-based, path-agnostic check. Add tests: passes when fully materialized, raises on a leftover meta tensor from a missing non-tied weight, and tolerates a tied weight resolved by tie_weights().
Summary
Adds first-class Ideogram 4 (text-to-image) support to InvokeAI — a new open-weight 9.3B single-stream DiT with a Qwen3-VL-8B text encoder and flow-matching sampler.
The defining trait of this model is that it is trained on a structured JSON prompt that describes the scene as a list of regions, each with a bounding box (
[y_min, x_min, y_max, x_max], normalized 0–1000, origin top-left) and a text description. To stay on the model's trained distribution, InvokeAI always feeds it structured JSON — even a plain prompt is wrapped into a one-element caption (see the safety-filter note below).The headline feature here is that this JSON is auto-assembled from the existing Canvas Regional Guidance layers: the global prompt becomes the overall description, and each enabled region contributes one element (its drawn rect → bbox, its prompt → description). Users can also paste raw JSON to drive the model directly (passed through unchanged). Assembly happens at generation time in a dedicated
ideogram4_caption_buildernode, so dynamic prompts / prompt batching that vary the global prompt are folded into the encoded caption, and each generated image records the exact JSON it was given.compositional_deconstruction.elementslist, or a single full-frame[0,0,1000,1000]element whosedescjust repeats the high-level description, returns an "Image blocked by safety filter" placeholder. The caption assembly is built to avoid this: it never emits bare plain text, and when no regions are drawn it synthesizes one element describing the whole scene with a partial bbox[100,100,900,900]. This is documented inmodels.mdx.Why the backend only ever sees one prompt string (no mask conditioning): Ideogram 4 does not use spatial attention masks for regions (unlike FLUX/SDXL/Z-Image regional guidance) — the region boxes are encoded as text inside the single JSON string fed to Qwen3-VL. So no mask-conditioning code is touched; the "regional" feature is purely caption assembly.
How
Backend (
invokeai/backend/ideogram4/, vendored from the Apache-2.0 reference, copyright headers retained):modeling_ideogram4.py), FLUX2-style KL VAE (autoencoder.py+latent_norm.py), logit-normal flow-match scheduler + presets (scheduler.py,sampler_configs.py), nf4/fp8 quantized loading, and InvokeAI-sidedenoise.py/text_encoding.py/sampling_utils.py/caption.py.[text]+[image]tokens, negative runs the unconditional transformer over image-only tokens with zeroed LLM features (v = gw·pos + (1−gw)·neg). ⇒ no negative prompt. Atransformer_pair.pywrapper keeps both transformers co-resident through the loop so the cache doesn't swap them every step (nf4 ≈ 10 GB resident during denoise; fits 24 GB). The guidance schedule caps its polish tail atnum_steps-1so at least one main (guided) step always remains, and requiressteps >= 2.BaseModelType.Ideogram4, a Qwen3-VL text-encoder type, config detector + diffusers config, and a loader mirroring Z-Image.ideogram4_model_loader,ideogram4_text_encoder,ideogram4_caption_builder(Python port of the JSON assembly),ideogram4_denoise,ideogram4_latents_to_image; newIdeogram4ConditioningInfo+ field/output;ideogram4_txt2imggeneration mode; a declaredideogram4_captionmetadata field.Frontend:
buildIdeogram4Prompt.ts—collectIdeogram4PromptInputs: gathers the raw inputs (global prompt, enabled regions →{prompt, bbox}clamped/rounded to 0–1000, color palette) for the caption builder node. The JSON assembly itself now lives in the backend node (single source of truth, Python-tested); raw-JSON passthrough and stable key order are handled there.buildIdeogram4Graph.ts— text2img-only graph builder + enqueue wiring:prompt (string) → ideogram4_caption_builder → text_encoder. The real prompt node carries the global prompt, so the linear-UI batch injector (dynamic prompts / prompt batching) writes expansions into it and they flow through the caption builder into the encoder — the earlier "decoy string node" hack is removed. The builder's output is wired to theideogram4_captionmetadata field via an edge, so each batched image records its actual caption.Dependencies: bumps
transformersto>=5.5,<5.6(Qwen3-VL landed in 4.57; the encoder needs it) andcompelto>=2.4.0,<3, with the necessary adaptations to the FLUX / Z-Image loaders, the safety checker, the HF metadata fetcher andmodel_util.Out of scope (v1):
txt2img). No latent/image init path exists in this PR.Related Issues / Discussions
transformers5.x bump tracked in PR feat - Migrate to Transformers 5.5.4 #9248 — this branch carries the same bump (>=5.5,<5.6) plus the cross-model adaptations it requires.QA Instructions
Requires the gated weights (
ideogram-ai/ideogram-4-nf4— nf4 is the 24 GB path, CUDA/bitsandbytes only) plus the Qwen3-VL encoder + VAE sub-dependencies.ideogram4_captionrow shows the assembled JSON with the correct key order andelements[*].bbox(0–1000,[y_min, x_min, y_max, x_max]) matching where you drew the boxes, and that element placement in the image roughly matches.ideogram4_captionmetadata: the prompt was wrapped into a one-element caption with a partial bbox[100,100,900,900]— this is the safety-filter workaround. Dynamic prompts / prompt batching still expand (they now flow through the real prompt node → caption builder).style_description.color_palette(auto-build mode only — ignored for raw JSON).Frontend gates (from
invokeai/frontend/web/):pnpm lintandpnpm test:no-watch(includes the caption/graph tests).tscclean. Backend:tests/backend/ideogram4/test_caption.pycovers the assembly (raw-JSON passthrough, region mapping, and the no-region default element).Merge Plan
transformersto>=5.5,<5.6andcompelto>=2.4.0,<3, which touches many models (FLUX, Z-Image, safety checker, HF metadata fetch). Coordinate with / sequence after PR feat - Migrate to Transformers 5.5.4 #9248 (the transformers 5.x bump) to avoid a double-bump conflict, and time it to not collide with a pending release. Broad regression QA across existing model types is warranted, not just Ideogram 4.starter_models.pyentries, and the optional local Magic Prompt node (blocked on the upstream system-prompts PR feat: add System Prompts library for Expand Prompt button #9152, whosemigration_32collides withmainand must be renumbered to 33 first).Checklist
test_caption.py) + frontend caption/graph testsmodels.mdxdocuments the model's built-in safety filterWhat's Newcopy (if doing a release after this PR)