Skip to content

Commit 5ff5939

Browse files
filipstrandCursor Assistant
andauthored
FIBO-Lite support (#366)
Co-authored-by: Cursor Assistant <assistant@cursor.com>
1 parent 479ace0 commit 5ff5939

File tree

11 files changed

+132
-18
lines changed

11 files changed

+132
-18
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ MFLUX supports the following model families. They have different strengths and w
119119

120120
| Model | Release date | Size | Type | Training | Description |
121121
| --- | --- | --- | --- | --- | --- |
122-
|[Z-Image](src/mflux/models/z_image/README.md) | Nov 2025 | 6B | Distilled & Base | Yes | Best all-rounder: fast, small, very good quality and realism. |
122+
|[Z-Image](src/mflux/models/z_image/README.md) | Nov 2025 | 6B | Distilled & Base | Yes | Fast, small, very good quality and realism. |
123123
|[FLUX.2](src/mflux/models/flux2/README.md) | Jan 2026 | 4B & 9B | Distilled & Base | Yes | Fastest + smallest with very good qaility and edit capabilities. |
124-
|[FIBO](src/mflux/models/fibo/README.md) | Oct 2025 | 8B | Base | No | Very good JSON-based prompt understanding and editability, medium speed |
124+
|[FIBO](src/mflux/models/fibo/README.md) | Oct 2025+ | 8B | Distilled & Base | No | Very good JSON-based prompt understanding and editability. |
125125
|[SeedVR2](src/mflux/models/seedvr2/README.md) | Jun 2025 | 3B & 7B || No | Best upscaling model. |
126126
|[Qwen Image](src/mflux/models/qwen/README.md) | Aug 2025+ | 20B | Base | No | Large model (slower); strong prompt understanding and world knowledge. Has edit capabilities |
127127
|[Depth Pro](src/mflux/models/depth_pro/README.md) | Oct 2024 ||| No | Very fast and accurate depth estimation model from Apple. |

src/mflux/cli/defaults/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"dev-krea",
2020
"qwen",
2121
"fibo",
22+
"fibo-lite",
2223
"z-image",
2324
"z-image-turbo",
2425
"flux2-klein-4b",
@@ -34,6 +35,7 @@
3435
"qwen-image": 20,
3536
"qwen-image-edit": 20,
3637
"fibo": 20,
38+
"fibo-lite": 8,
3739
"z-image": 50,
3840
"z-image-turbo": 9,
3941
"flux2-klein-4b": 4,

src/mflux/models/common/config/model_config.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ def qwen_image_edit() -> "ModelConfig":
137137
def fibo() -> "ModelConfig":
138138
return AVAILABLE_MODELS["fibo"]
139139

140+
@staticmethod
141+
@lru_cache
142+
def fibo_lite() -> "ModelConfig":
143+
return AVAILABLE_MODELS["fibo-lite"]
144+
140145
@staticmethod
141146
@lru_cache
142147
def z_image_turbo() -> "ModelConfig":
@@ -453,8 +458,20 @@ def from_name(
453458
supports_guidance=True,
454459
requires_sigma_shift=False,
455460
),
456-
"z-image": ModelConfig(
461+
"fibo-lite": ModelConfig(
457462
priority=18,
463+
aliases=["fibo-lite", "fibo_lite"],
464+
model_name="briaai/Fibo-lite",
465+
base_model=None,
466+
controlnet_model=None,
467+
custom_transformer_model=None,
468+
num_train_steps=1000,
469+
max_sequence_length=512,
470+
supports_guidance=True,
471+
requires_sigma_shift=False,
472+
),
473+
"z-image": ModelConfig(
474+
priority=19,
458475
aliases=["z-image", "zimage"],
459476
model_name="Tongyi-MAI/Z-Image",
460477
base_model=None,
@@ -466,7 +483,7 @@ def from_name(
466483
requires_sigma_shift=True,
467484
),
468485
"z-image-turbo": ModelConfig(
469-
priority=19,
486+
priority=20,
470487
aliases=["z-image-turbo", "zimage-turbo"],
471488
model_name="Tongyi-MAI/Z-Image-Turbo",
472489
base_model=None,
@@ -478,7 +495,7 @@ def from_name(
478495
requires_sigma_shift=True,
479496
),
480497
"seedvr2-3b": ModelConfig(
481-
priority=20,
498+
priority=21,
482499
aliases=["seedvr2-3b", "seedvr2"],
483500
model_name="numz/SeedVR2_comfyUI",
484501
base_model=None,
@@ -490,7 +507,7 @@ def from_name(
490507
requires_sigma_shift=None,
491508
),
492509
"seedvr2-7b": ModelConfig(
493-
priority=21,
510+
priority=22,
494511
aliases=["seedvr2-7b", "seedvr2-7B"],
495512
model_name="numz/SeedVR2_comfyUI",
496513
base_model=None,

src/mflux/models/fibo/README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,38 @@ Most text-to-image models excel at imagination—but not control. FIBO is traine
1313
- **Strong prompt adherence**: High alignment on PRISM-style evaluations
1414
- **Enterprise-grade**: 100% licensed data with governance, repeatability, and legal clarity
1515

16+
## FIBO Lite
17+
18+
[FIBO Lite](https://huggingface.co/briaai/Fibo-lite) is a two-stage distilled variant combining CFG distillation and SCFM for fast few-step generation. Use `--model fibo-lite` for ~10x speed: 8 steps, `guidance=1.0`, no negative prompt needed. Slight quality tradeoff vs. base FIBO.
19+
20+
```sh
21+
mflux-generate-fibo \
22+
--model fibo-lite \
23+
--prompt "A tiny watercolor robot in a garden" \
24+
--steps 8 \
25+
--seed 42
26+
```
27+
28+
<details>
29+
<summary>Python API</summary>
30+
31+
```python
32+
from mflux.models.common.config import ModelConfig
33+
from mflux.models.fibo.variants.txt2img.fibo import FIBO
34+
from mflux.models.fibo_vlm.model.fibo_vlm import FiboVLM
35+
36+
vlm = FiboVLM()
37+
json_prompt = vlm.generate(prompt="A tiny watercolor robot in a garden", seed=42)
38+
model = FIBO(model_config=ModelConfig.fibo_lite())
39+
image = model.generate_image(
40+
seed=42,
41+
prompt=json_prompt,
42+
num_inference_steps=8,
43+
)
44+
image.save("robot_lite.png")
45+
```
46+
</details>
47+
1648
## The three modes: Generate, Refine, and Inspire
1749

1850
### Generate
@@ -334,5 +366,5 @@ image.save("bird_inspired.png")
334366

335367
## Notes
336368
> [!WARNING]
337-
> FIBO requires downloading the `briaai/FIBO` model weights (~24GB) and the `briaai/FIBO-vlm` vision-language model (~8GB), totaling ~32GB for the full model, or use quantization for smaller sizes.
369+
> FIBO requires downloading the `briaai/FIBO` or `briaai/FIBO-lite` model weights (~24GB) and the `briaai/FIBO-vlm` vision-language model (~8GB), totaling ~32GB for the full model, or use quantization for smaller sizes.
338370

src/mflux/models/fibo/cli/fibo_generate.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from mflux.callbacks.callback_manager import CallbackManager
22
from mflux.cli.defaults import defaults as ui_defaults
33
from mflux.cli.parser.parsers import CommandLineParser
4+
from mflux.models.common.config.model_config import ModelConfig
45
from mflux.models.fibo.latent_creator.fibo_latent_creator import FiboLatentCreator
56
from mflux.models.fibo.variants.txt2img.fibo import FIBO
67
from mflux.models.fibo.variants.txt2img.util import FiboUtil
@@ -22,14 +23,23 @@ def main():
2223

2324
# 0. Set default guidance value if not provided by user
2425
if args.guidance is None:
25-
args.guidance = ui_defaults.GUIDANCE_SCALE
26+
if args.model == "fibo-lite":
27+
args.guidance = 1.0 # distilled, no CFG
28+
elif args.model == "fibo":
29+
args.guidance = 5.0 # base FIBO typical
30+
else:
31+
args.guidance = ui_defaults.GUIDANCE_SCALE
32+
33+
resolved_model_name = args.model if args.model in ui_defaults.MODEL_CHOICES else "fibo"
34+
model_config = ModelConfig.from_name(model_name=resolved_model_name, base_model=args.base_model)
2635

2736
json_prompt = FiboUtil.get_json_prompt(args, quantize=args.quantize)
2837

2938
# 1. Load the FIBO model
3039
fibo = FIBO(
3140
quantize=args.quantize,
3241
model_path=args.model_path,
42+
model_config=model_config,
3343
)
3444

3545
# 2. Register callbacks

src/mflux/models/fibo/model/fibo_text_encoder/prompt_encoder.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,11 @@ def encode_prompt(
1414
negative_prompt: str | None,
1515
tokenizer: Tokenizer,
1616
text_encoder: SmolLM3_3B_TextEncoder,
17+
guidance: float = 4.0,
1718
) -> tuple[str, mx.array, List[mx.array]]:
18-
# 0. Set default negative prompt if not provided
19-
if negative_prompt is None or negative_prompt == "":
20-
negative_prompt = "ugly, blurry, low quality"
21-
22-
# 1. Convert prompt to JSON format
2319
json.loads(prompt)
2420
json_prompt = prompt
2521

26-
# 2. Get prompt embeddings for positive and negative prompt
2722
prompt_embeds, prompt_layers, prompt_attention_mask = PromptEncoder._get_prompt_embeds(
2823
prompt=json_prompt,
2924
tokenizer=tokenizer,
@@ -32,6 +27,18 @@ def encode_prompt(
3227
max_sequence_length=2048,
3328
tokenization_prefix="positive",
3429
)
30+
31+
if guidance == 1.0:
32+
encoder_hidden_states, prompt_layers = PromptEncoder._prepare_positive_only_output(
33+
prompt_embeds=prompt_embeds,
34+
prompt_layers=prompt_layers,
35+
prompt_attention_mask=prompt_attention_mask,
36+
)
37+
return json_prompt, encoder_hidden_states, prompt_layers
38+
39+
if negative_prompt is None or negative_prompt == "":
40+
negative_prompt = "ugly, blurry, low quality"
41+
3542
neg_prompt_embeds, neg_prompt_layers, neg_prompt_attention_mask = PromptEncoder._get_prompt_embeds(
3643
prompt=negative_prompt,
3744
tokenizer=tokenizer,
@@ -54,6 +61,27 @@ def encode_prompt(
5461
)
5562
return json_prompt, encoder_hidden_states, prompt_layers
5663

64+
@staticmethod
65+
def _prepare_positive_only_output(
66+
prompt_embeds: mx.array,
67+
prompt_layers: List[mx.array],
68+
prompt_attention_mask: mx.array,
69+
) -> tuple[mx.array, List[mx.array]]:
70+
"""Prepare encoder output when guidance=1.0 (no negative prompt, positive only)."""
71+
max_tokens = prompt_embeds.shape[1]
72+
encoder_hidden_states, _ = PromptEncoder._pad_embedding(
73+
prompt_embeds=prompt_embeds,
74+
max_tokens=max_tokens,
75+
attention_mask=prompt_attention_mask,
76+
)
77+
prompt_layers = [PromptEncoder._pad_embedding(layer, max_tokens)[0] for layer in prompt_layers]
78+
total_num_layers_transformer = 46
79+
if len(prompt_layers) >= total_num_layers_transformer:
80+
prompt_layers = prompt_layers[len(prompt_layers) - total_num_layers_transformer :]
81+
else:
82+
prompt_layers = prompt_layers + [prompt_layers[-1]] * (total_num_layers_transformer - len(prompt_layers))
83+
return encoder_hidden_states, prompt_layers
84+
5785
@staticmethod
5886
def _get_encoder_hidden_states(neg_prompt_attention_mask, neg_prompt_embeds, prompt_attention_mask, prompt_embeds):
5987
max_tokens = max(neg_prompt_embeds.shape[1], prompt_embeds.shape[1])

src/mflux/models/fibo/variants/txt2img/fibo.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,13 @@ def generate_image(
5757
negative_prompt: str | None = None,
5858
) -> GeneratedImage:
5959
# 0. Create a new config based on the model type and input parameters
60+
effective_guidance = guidance
61+
if "fibo-lite" in self.model_config.aliases:
62+
effective_guidance = 1.0 # distilled model, cond-only
6063
config = Config(
6164
width=width,
6265
height=height,
63-
guidance=guidance,
66+
guidance=effective_guidance,
6467
scheduler=scheduler,
6568
image_path=image_path,
6669
image_strength=image_strength,
@@ -89,6 +92,7 @@ def generate_image(
8992
negative_prompt=negative_prompt,
9093
tokenizer=self.tokenizers["fibo"],
9194
text_encoder=self.text_encoder,
95+
guidance=config.guidance,
9296
)
9397

9498
# 3. Create callback context and call before_loop
@@ -105,7 +109,8 @@ def generate_image(
105109
text_encoder_layers=text_encoder_layers,
106110
encoder_hidden_states=encoder_hidden_states,
107111
)
108-
noise = FIBO._apply_classifier_free_guidance(noise, config.guidance)
112+
if config.guidance != 1.0:
113+
noise = FIBO._apply_classifier_free_guidance(noise, config.guidance)
109114

110115
# 5.t Take one denoise step
111116
latents = config.scheduler.step(noise=noise, timestep=t, latents=latents)

src/mflux/utils/generated_image.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ def _format_redux_strengths(self) -> list[float] | None:
152152
return [round(scale, 2) for scale in self.redux_image_strengths]
153153

154154
def _is_fibo_model(self) -> bool:
155-
return self.model_config.model_name == "briaai/FIBO" or str(self.model_config.base_model) == "fibo"
155+
name = self.model_config.model_name
156+
return name == "briaai/FIBO" or name == "briaai/Fibo-lite" or str(self.model_config.base_model) == "fibo"
156157

157158
def _save_prompt_file(self, image_path: str | Path, overwrite: bool) -> None:
158159
file_path = Path(image_path)

tests/image_generation/helpers/image_generation_fibo_test_helper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pathlib import Path
33
from typing import Optional
44

5+
from mflux.models.common.config import ModelConfig
56
from mflux.models.fibo.variants.txt2img.fibo import FIBO
67
from mflux.utils.image_compare import ImageCompare
78

@@ -20,8 +21,9 @@ def assert_matches_reference_image(
2021
negative_prompt: Optional[str] = None,
2122
mismatch_threshold: Optional[float] = None,
2223
quantize: Optional[int] = None,
24+
model_config: Optional[ModelConfig] = None,
2325
):
24-
# resolve paths
26+
model_config = model_config or ModelConfig.fibo()
2527
reference_image_path = ImageGeneratorFiboTestHelper.resolve_path(reference_image_path)
2628
output_image_path = ImageGeneratorFiboTestHelper.resolve_path(output_image_path)
2729

@@ -30,6 +32,7 @@ def assert_matches_reference_image(
3032
model = FIBO(
3133
quantize=quantize,
3234
model_path=None,
35+
model_config=model_config,
3336
)
3437

3538
# Step 2: Generate image from prompt

tests/image_generation/test_generate_image_fibo.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from mflux.models.common.config import ModelConfig
34
from tests.image_generation.helpers.image_generation_fibo_test_helper import ImageGeneratorFiboTestHelper
45

56
OWL_PROMPT = """
@@ -123,3 +124,18 @@ def test_image_generation_fibo_refined_white_owl(self):
123124
guidance=4.0,
124125
quantize=8,
125126
)
127+
128+
@pytest.mark.slow
129+
def test_image_generation_fibo_lite(self):
130+
ImageGeneratorFiboTestHelper.assert_matches_reference_image(
131+
reference_image_path="reference_fibo_lite.png",
132+
output_image_path="output_fibo_lite.png",
133+
prompt=OWL_PROMPT,
134+
steps=8,
135+
seed=42,
136+
height=352,
137+
width=640,
138+
guidance=1.0,
139+
quantize=8,
140+
model_config=ModelConfig.fibo_lite(),
141+
)

0 commit comments

Comments
 (0)