A comprehensive collection of custom nodes for ComfyUI focused on storytelling, scene management, and prompt templating workflows.
.txt per imagecd ComfyUI/custom_nodes
git clone https://github.com/frost-byte/comfyui-fbTools.git
ComfyUI-SCAIL-Pose - Required for NLF (Neural Lifting Framework) 3D pose generation
taichi for faster GPU-accelerated renderingComfyUI-WanVideoWrapper - Required only for LoRA functionality in Scene nodes
The LoraStackApply node requires comfy.sd.load_lora_for_models for direct-apply targets (LTX2.3, Flux2/Klein, Qwen, Z-Image) — available in the standard ComfyUI install.
For Wan2.2-Native targets, connect the lora_stack output to easy-use's loraStack node.
For Wan2.2-Wrapper targets, connect the wanvid_lora output to ComfyUI-WanVideoWrapper.
Install only if you use the Subject Compositor / SubjectLayerDefine nodes and want automatic background removal:
# BiRefNet via rembg (recommended — GPU-accelerated)
pip install "rembg[gpu]"
# CPU-only fallback
pip install rembg
Models (~100–400 MB each) are downloaded on first use and cached in ~/.u2net/ or the rembg cache directory.
If you prefer to supply your own mask, leave remove_background off and connect any upstream MASK output to SubjectLayerDefine.
Install one or more of these only if you use Dataset Captioning nodes:
# Qwen2.5-VL (recommended; image-focused, ~16GB VRAM in bf16)
pip install "transformers>=4.50.0" accelerate qwen-vl-utils
# Qwen2.5-Omni (heavier omni model, ~20GB VRAM)
pip install "transformers>=4.50.0" accelerate qwen-omni-utils
# Gemini Flash (cloud, no local VRAM needed)
pip install google-generativeai
export GEMINI_API_KEY=your_key_here
# Optional: 8-bit quantization (~50% VRAM reduction)
pip install bitsandbytes
Installation via ComfyUI-Manager:
All nodes are organized under the 🧊 frost-byte category in ComfyUI.
🧊 frost-byte/Story)🧊 frost-byte/Scene)🧊 frost-byte/Libber)🧊 frost-byte/compositing)SUBJECT_LAYER token consumed by SubjectCompositor.SUBJECT_LAYER inputs onto a canvas. Outputs a merged composite image, an individual_images batch (one per layer), a layer_count int, and the snapped canvas dimensions.🧊 frost-byte/lora)enabled toggle, audio weight guard (LTX2.3), and separate model/clip strengths.LORA_ENTRY inputs into a JSON string for scene persistence. Autogrow inputs. Optionally merges with existing JSON.model_target; routes to direct model patching, LORA_STACK (easy-use), or WANVIDLORA (WanVideoWrapper) output as appropriate..txt per imagedataset_summary.csvAll testing documentation is in docs/testing/:
The Libber system provides a powerful template/substitution mechanism for reusable text snippets:
Example:
# Define templates
libs = {
"chunky": "incredibly thick, and %yummy%",
"yummy": "delicious",
"character": "A %chunky% warrior"
}
# Apply substitutions
"Look at this %character%!"
# Result: "Look at this A incredibly thick, and delicious warrior!"
Features:
%)Each scene can have:
The new prompt system supports unlimited named prompts with metadata:
collection = PromptCollection()
collection.add_prompt(
"lighting",
"soft diffused lighting, golden hour",
category="scene",
description="Lighting setup",
tags=["lighting", "atmosphere"]
)
Features:
Compose two or more subjects onto a single canvas for use with image-conditioning workflows (LTX-Video, Qwen Edit, ReferenceLatent, etc.).
[SubjectLayerDefine] [SubjectLayerDefine] ...up to 20
| |
└─────── layer_0 ────┘
↓
[SubjectCompositor]
↓
composite / individual_images / layer_count
| Input | Type | Default | Notes |
|---|---|---|---|
image | IMAGE | — | Input image |
mask | MASK | optional | Pre-computed alpha mask. Overrides bg removal if connected. |
pad_top/bottom/left/right | FLOAT | 0.0 | Padding as fraction of longer dimension. 0.2 = 20% padding. |
offset_x | FLOAT | 0.0 | Horizontal offset. 0=center, 1.0=right edge, −1.0=left edge. |
offset_y | FLOAT | 0.0 | Vertical offset. 0=center, 1.0=bottom, −1.0=top. |
remove_background | BOOLEAN | True | Auto background removal via rembg/BiRefNet. |
bg_model | COMBO | BiRefNet-general | Background removal model. |
Output: SUBJECT_LAYER (custom type passed to SubjectCompositor)
| Input | Type | Default | Notes |
|---|---|---|---|
canvas_width | INT | 1344 | Target width. Snapped to divisible_by. |
canvas_height | INT | 768 | Target height. Snapped to divisible_by. |
canvas_color | STRING | #222222 | Background color. Accepts hex, named colors, "transparent". |
output_mode | COMBO | both | composite / individual / both |
divisible_by | INT | 32 | Snap dimensions to this multiple. 32 for LTX/video models. |
layer_0..N | SUBJECT_LAYER | — | Autogrow inputs. Connect SubjectLayerDefine outputs. |
Outputs: composite (IMAGE), individual_images (IMAGE batch [N, H, W, 3]), layer_count (INT)
Padding is specified as a fraction of the image's longer dimension:
pad = 0.2, image = 800×600
longer = 800
pad_pixels = 0.2 × 800 = 160 px added on that side
This makes the subject appear smaller relative to the canvas and to other layers — the primary use case for selectively scaling subjects down.
(−1, −1) ── (0, −1) ── (1, −1)
| | |
(−1, 0) ── (0, 0) ── (1, 0) ← center of canvas
| | |
(−1, 1) ── (0, 1) ── (1, 1)
The subject's center is placed at the computed canvas position. Values beyond ±1.0 are allowed and will partially clip the subject at the edge.
| Mode | Use when… |
|---|---|
composite | Feeding a single merged image to ReferenceLatent or TextEncoderQwenImageEditPlus |
individual | Feeding separate per-subject images to multiple ReferenceLatent nodes or a multi-reference conditioning node |
both | You want flexibility without re-running the compositor |
| Model | Best for |
|---|---|
| BiRefNet-general | General subjects, objects, scenery |
| BiRefNet-portrait | Human faces and portraits |
| BiRefNet-general-lite | Faster, slightly lower quality |
| u2net | General — good fallback |
| u2net_human_seg | Human silhouettes |
| isnet-general-use | High-detail foreground extraction |
If your workflow already has a bg-removal node (e.g. RMBG, BiRefNet from ComfyUI-BRIA),
connect its MASK output to the mask input of SubjectLayerDefine.
The remove_background flag is ignored when a mask is connected.
Persist and apply LoRA settings per model target. Replaces the abandoned LTX2MasterLoaderLD and consolidates LoRA management across LTX2.3, Wan2.2 (Native and Wrapper), Flux2/Klein, Qwen, and Z-Image.
# Scene definition (save once)
LoraEntryDefine (LTX2.3, my_character.safetensors, audio_enabled=True)
↓ lora_entry
LoraEntryDefine (Wan2.2-Native, my_character_wan.safetensors)
↓ lora_entry
LoraStackCollect
↓ stack_json → [Scene Node / String storage]
↓ lora_stack_data
# Inference (LTX2.3 pipeline)
[Scene Node] → stack_json
↓
LoraStackApply (model_target=LTX2.3)
model ← [your model]
clip ← [your clip]
↓ model → [LTX2.3 sampler]
↓ clip → [text encoder]
| Input | Default | Notes |
|---|---|---|
lora | None | File picker from loras folder |
model_target | LTX2.3 | Which pipeline this LoRA applies to |
strength_model | 1.0 | UNet/transformer weight strength |
strength_clip | 1.0 | Text encoder strength (ignored where N/A) |
enabled | True | Toggle off without removing from stack |
audio_enabled | True | LTX2.3 only: include audio weights |
Output: LORA_ENTRY (custom type)
| Input | Notes |
|---|---|
entry_0..N | Autogrow LORA_ENTRY inputs (up to 20) |
existing_json | Optional — merge with existing scene JSON |
Outputs: lora_stack_data (LORA_STACK_DATA), stack_json (STRING), entry_count (INT)
| Input | Notes |
|---|---|
model_target | Must match the target you set in LoraEntryDefine |
lora_stack_data | Connect from LoraStackCollect (preferred) |
stack_json | OR a JSON STRING from a scene node |
model | MODEL to patch (optional) |
clip | CLIP to patch (optional) |
prev_lora_stack | Wan2.2-Native: chain from existing LORA_STACK |
prev_wanvid_lora | Wan2.2-Wrapper: chain from existing WANVIDLORA |
low_mem_load | Wan2.2-Wrapper infrastructure setting |
merge_loras | Wan2.2-Wrapper infrastructure setting |
Outputs: model (MODEL), clip (CLIP), lora_stack (LORA_STACK), wanvid_lora (WANVIDLORA), applied_count (INT)
| Target | model | clip | lora_stack | wanvid_lora |
|---|---|---|---|---|
| LTX2.3 | ✓ patched | ✓ patched | — | — |
| Wan2.2-Native | passthrough | passthrough | ✓ built | — |
| Wan2.2-Wrapper | passthrough | passthrough | — | ✓ built |
| Flux2/Klein | ✓ patched | ✓ patched | — | — |
| Qwen | ✓ patched | ✓ patched | — | — |
| Z-Image | ✓ patched | ✓ patched | — | — |
The audio_enabled flag controls whether audio-related weight keys are included. When audio_enabled=False, keys containing these strings are stripped: audio, vocoder, speech, audio_stream, cross_modal, video_to_audio, av_ca.
This replicates the behaviour of the abandoned LTX2MasterLoaderLD node, now per-LoRA with V3 API.
Use this flow when preparing LoRA training captions:
[Dataset Captioner]
|
v
[Dataset Caption Editor] <- optional post-processing (trigger word, find/replace)
|
v
[Dataset Caption Viewer] <- review/edit/re-caption individual images
|
v
[Dataset Export Summary] <- verify coverage and caption statistics
Then feed your dataset directory into your training configuration.
Dataset Captioner supports:
input_directory, output_directory, recursivecaptioner_type: qwen_vl (recommended), qwen_omni, or gemini_flashinstruction, trigger_word, clean_captiondevice: auto, cuda, or cpuuse_8bit (requires bitsandbytes)override_existing, unload_after, gemini_api_keyOutputs: dataset_path, caption_count, failed_count
Dataset Caption Editor runs in dry-run mode by default (dry_run=true) and only writes changes when disabled.
Dataset Caption Viewer provides thumbnail rows, caption editing, per-image re-caption, and clear-caption actions.
The current viewer table viewport is intentionally fixed-height for layout stability; the table scrolls internally.
Dataset Export Summary reports total/captioned/missing counts and caption length stats; set export_csv=true to write dataset_summary.csv.
Use scripts/dataset_caption_edit.fish for repeatable multi-pass find/replace edits against the /fbtools/dataset_caption/edit API.
# Dry-run (default)
fish scripts/dataset_caption_edit.fish --dataset rara \
--pass 'old phrase=>new phrase' \
--pass 'another old=>another new'
# Apply changes
fish scripts/dataset_caption_edit.fish --dataset rara --apply \
--pass 'old phrase=>new phrase' \
--pass 'another old=>another new'
Notes:
find=>replace.find_text and replace_text fields expected by the API.--output <dir> when captions are stored in a separate output directory.| Model | Precision | Approx VRAM |
|---|---|---|
| Qwen2.5-VL-7B | bf16 | ~16 GB |
| Qwen2.5-VL-7B | 8-bit | ~8 GB |
| Qwen2.5-Omni-7B | bf16 | ~20 GB |
| Qwen2.5-Omni-7B | 8-bit | ~11 GB |
| Gemini Flash | cloud | 0 GB |
trigger_word in Dataset Captioner instead of relying on prompt wording for consistency.Dataset Caption Viewer to correct hallucinations before training.Dataset Export Summary to validate.To install development dependencies and pre-commit hooks:
cd comfyui-fbTools
pip install -e .[dev]
pre-commit install
The -e flag installs in "editable" mode, so changes are immediately reflected when ComfyUI restarts.
comfyui-fbTools/
├── extension.py # Main Python extension with all nodes
├── prompt_models.py # Data models (PromptMetadata, PromptCollection)
├── utils/ # Python utilities
│ ├── io.py # File I/O operations
│ ├── util.py # General utilities
│ ├── pose.py # Pose detection utilities
│ └── images.py # Image processing utilities
├── js/ # JavaScript frontend code
│ ├── fb_tools.js # Main extension registration
│ ├── api/ # REST API clients
│ │ ├── libber.js # Libber API
│ │ ├── prompt_collection.js # PromptCollection API
│ │ ├── scene.js # Scene API
│ │ └── story.js # Story API
│ ├── nodes/ # Node-specific handlers
│ │ ├── libber.js # LibberManager & LibberApply
│ │ ├── scene.js # SceneSelect handler
│ │ └── story.js # StoryEdit & StoryView handlers
│ └── utils/ # Shared JavaScript utilities
├── tests/ # Python unit tests
│ ├── test_prompt_collection.py
│ └── test_libber.py
└── js-tests/ # JavaScript unit tests
├── prompt_collection_api.test.js
└── libber_api.test.js
# Run all tests
python -m pytest tests/ -v
# Run specific test file
python -m pytest tests/test_libber.py -v
# With coverage report
python -m pytest tests/ --cov=. --cov-report=html
Test Coverage:
See Testing Guide for detailed instructions.
cd js/
npm install # First time only
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage
Test Coverage:
See TEST_RESULTS.md for detailed test coverage.
The project uses:
See Frontend Documentation for architecture details.
If you wish to share this extension:
pyproject.tomlREGISTRY_ACCESS_TOKEN to GitHub SecretsSee ComfyUI Registry docs for details.
See LICENSE file.
See CHANGELOG.md for version history and release notes.
2026-04-09: LoRA Scene Nodes
LoraEntryDefine — define one LoRA for a specific model target with per-LoRA audio guard, enable toggle, and separate model/clip strengthsLoraStackCollect — collect up to 20 LORA_ENTRY inputs into a persisted JSON stack with optional merge from existing JSONLoraStackApply — apply persisted stack at inference time; routes to direct patching (LTX2.3, Flux, Qwen, Z-Image), LORA_STACK (Wan2.2-Native), or WANVIDLORA (Wan2.2-Wrapper)LORA_ENTRY and LORA_STACK_DATA types for type-safe wiringLTX2MasterLoaderLD with V3 API per-LoRA granularity2026-04-08: Subject Compositor Nodes
SubjectLayerDefine — define a subject layer with fractional padding, canvas offset, and optional background removalSubjectCompositor — composite 1–20 layers onto a canvas (composite, individual, or both output modes)SUBJECT_LAYER type wiring between the two nodesdivisible_by, default 32 for LTX/video models)2025-01-18: Generic Mask System
2024-12-19: Libber System Overhaul
Previous Updates:
296 commits
Python
63.0%
JavaScript
33.4%
CSS
3.5%
A comprehensive collection of custom nodes for ComfyUI focused on storytelling, scene management, and prompt templating workflows.
.txt per imagecd ComfyUI/custom_nodes
git clone https://github.com/frost-byte/comfyui-fbTools.git
ComfyUI-SCAIL-Pose - Required for NLF (Neural Lifting Framework) 3D pose generation
taichi for faster GPU-accelerated renderingComfyUI-WanVideoWrapper - Required only for LoRA functionality in Scene nodes
The LoraStackApply node requires comfy.sd.load_lora_for_models for direct-apply targets (LTX2.3, Flux2/Klein, Qwen, Z-Image) — available in the standard ComfyUI install.
For Wan2.2-Native targets, connect the lora_stack output to easy-use's loraStack node.
For Wan2.2-Wrapper targets, connect the wanvid_lora output to ComfyUI-WanVideoWrapper.
Install only if you use the Subject Compositor / SubjectLayerDefine nodes and want automatic background removal:
# BiRefNet via rembg (recommended — GPU-accelerated)
pip install "rembg[gpu]"
# CPU-only fallback
pip install rembg
Models (~100–400 MB each) are downloaded on first use and cached in ~/.u2net/ or the rembg cache directory.
If you prefer to supply your own mask, leave remove_background off and connect any upstream MASK output to SubjectLayerDefine.
Install one or more of these only if you use Dataset Captioning nodes:
# Qwen2.5-VL (recommended; image-focused, ~16GB VRAM in bf16)
pip install "transformers>=4.50.0" accelerate qwen-vl-utils
# Qwen2.5-Omni (heavier omni model, ~20GB VRAM)
pip install "transformers>=4.50.0" accelerate qwen-omni-utils
# Gemini Flash (cloud, no local VRAM needed)
pip install google-generativeai
export GEMINI_API_KEY=your_key_here
# Optional: 8-bit quantization (~50% VRAM reduction)
pip install bitsandbytes
Installation via ComfyUI-Manager:
All nodes are organized under the 🧊 frost-byte category in ComfyUI.
🧊 frost-byte/Story)🧊 frost-byte/Scene)🧊 frost-byte/Libber)🧊 frost-byte/compositing)SUBJECT_LAYER token consumed by SubjectCompositor.SUBJECT_LAYER inputs onto a canvas. Outputs a merged composite image, an individual_images batch (one per layer), a layer_count int, and the snapped canvas dimensions.🧊 frost-byte/lora)enabled toggle, audio weight guard (LTX2.3), and separate model/clip strengths.LORA_ENTRY inputs into a JSON string for scene persistence. Autogrow inputs. Optionally merges with existing JSON.model_target; routes to direct model patching, LORA_STACK (easy-use), or WANVIDLORA (WanVideoWrapper) output as appropriate..txt per imagedataset_summary.csvAll testing documentation is in docs/testing/:
The Libber system provides a powerful template/substitution mechanism for reusable text snippets:
Example:
# Define templates
libs = {
"chunky": "incredibly thick, and %yummy%",
"yummy": "delicious",
"character": "A %chunky% warrior"
}
# Apply substitutions
"Look at this %character%!"
# Result: "Look at this A incredibly thick, and delicious warrior!"
Features:
%)Each scene can have:
The new prompt system supports unlimited named prompts with metadata:
collection = PromptCollection()
collection.add_prompt(
"lighting",
"soft diffused lighting, golden hour",
category="scene",
description="Lighting setup",
tags=["lighting", "atmosphere"]
)
Features:
Compose two or more subjects onto a single canvas for use with image-conditioning workflows (LTX-Video, Qwen Edit, ReferenceLatent, etc.).
[SubjectLayerDefine] [SubjectLayerDefine] ...up to 20
| |
└─────── layer_0 ────┘
↓
[SubjectCompositor]
↓
composite / individual_images / layer_count
| Input | Type | Default | Notes |
|---|---|---|---|
image | IMAGE | — | Input image |
mask | MASK | optional | Pre-computed alpha mask. Overrides bg removal if connected. |
pad_top/bottom/left/right | FLOAT | 0.0 | Padding as fraction of longer dimension. 0.2 = 20% padding. |
offset_x | FLOAT | 0.0 | Horizontal offset. 0=center, 1.0=right edge, −1.0=left edge. |
offset_y | FLOAT | 0.0 | Vertical offset. 0=center, 1.0=bottom, −1.0=top. |
remove_background | BOOLEAN | True | Auto background removal via rembg/BiRefNet. |
bg_model | COMBO | BiRefNet-general | Background removal model. |
Output: SUBJECT_LAYER (custom type passed to SubjectCompositor)
| Input | Type | Default | Notes |
|---|---|---|---|
canvas_width | INT | 1344 | Target width. Snapped to divisible_by. |
canvas_height | INT | 768 | Target height. Snapped to divisible_by. |
canvas_color | STRING | #222222 | Background color. Accepts hex, named colors, "transparent". |
output_mode | COMBO | both | composite / individual / both |
divisible_by | INT | 32 | Snap dimensions to this multiple. 32 for LTX/video models. |
layer_0..N | SUBJECT_LAYER | — | Autogrow inputs. Connect SubjectLayerDefine outputs. |
Outputs: composite (IMAGE), individual_images (IMAGE batch [N, H, W, 3]), layer_count (INT)
Padding is specified as a fraction of the image's longer dimension:
pad = 0.2, image = 800×600
longer = 800
pad_pixels = 0.2 × 800 = 160 px added on that side
This makes the subject appear smaller relative to the canvas and to other layers — the primary use case for selectively scaling subjects down.
(−1, −1) ── (0, −1) ── (1, −1)
| | |
(−1, 0) ── (0, 0) ── (1, 0) ← center of canvas
| | |
(−1, 1) ── (0, 1) ── (1, 1)
The subject's center is placed at the computed canvas position. Values beyond ±1.0 are allowed and will partially clip the subject at the edge.
| Mode | Use when… |
|---|---|
composite | Feeding a single merged image to ReferenceLatent or TextEncoderQwenImageEditPlus |
individual | Feeding separate per-subject images to multiple ReferenceLatent nodes or a multi-reference conditioning node |
both | You want flexibility without re-running the compositor |
| Model | Best for |
|---|---|
| BiRefNet-general | General subjects, objects, scenery |
| BiRefNet-portrait | Human faces and portraits |
| BiRefNet-general-lite | Faster, slightly lower quality |
| u2net | General — good fallback |
| u2net_human_seg | Human silhouettes |
| isnet-general-use | High-detail foreground extraction |
If your workflow already has a bg-removal node (e.g. RMBG, BiRefNet from ComfyUI-BRIA),
connect its MASK output to the mask input of SubjectLayerDefine.
The remove_background flag is ignored when a mask is connected.
Persist and apply LoRA settings per model target. Replaces the abandoned LTX2MasterLoaderLD and consolidates LoRA management across LTX2.3, Wan2.2 (Native and Wrapper), Flux2/Klein, Qwen, and Z-Image.
# Scene definition (save once)
LoraEntryDefine (LTX2.3, my_character.safetensors, audio_enabled=True)
↓ lora_entry
LoraEntryDefine (Wan2.2-Native, my_character_wan.safetensors)
↓ lora_entry
LoraStackCollect
↓ stack_json → [Scene Node / String storage]
↓ lora_stack_data
# Inference (LTX2.3 pipeline)
[Scene Node] → stack_json
↓
LoraStackApply (model_target=LTX2.3)
model ← [your model]
clip ← [your clip]
↓ model → [LTX2.3 sampler]
↓ clip → [text encoder]
| Input | Default | Notes |
|---|---|---|
lora | None | File picker from loras folder |
model_target | LTX2.3 | Which pipeline this LoRA applies to |
strength_model | 1.0 | UNet/transformer weight strength |
strength_clip | 1.0 | Text encoder strength (ignored where N/A) |
enabled | True | Toggle off without removing from stack |
audio_enabled | True | LTX2.3 only: include audio weights |
Output: LORA_ENTRY (custom type)
| Input | Notes |
|---|---|
entry_0..N | Autogrow LORA_ENTRY inputs (up to 20) |
existing_json | Optional — merge with existing scene JSON |
Outputs: lora_stack_data (LORA_STACK_DATA), stack_json (STRING), entry_count (INT)
| Input | Notes |
|---|---|
model_target | Must match the target you set in LoraEntryDefine |
lora_stack_data | Connect from LoraStackCollect (preferred) |
stack_json | OR a JSON STRING from a scene node |
model | MODEL to patch (optional) |
clip | CLIP to patch (optional) |
prev_lora_stack | Wan2.2-Native: chain from existing LORA_STACK |
prev_wanvid_lora | Wan2.2-Wrapper: chain from existing WANVIDLORA |
low_mem_load | Wan2.2-Wrapper infrastructure setting |
merge_loras | Wan2.2-Wrapper infrastructure setting |
Outputs: model (MODEL), clip (CLIP), lora_stack (LORA_STACK), wanvid_lora (WANVIDLORA), applied_count (INT)
| Target | model | clip | lora_stack | wanvid_lora |
|---|---|---|---|---|
| LTX2.3 | ✓ patched | ✓ patched | — | — |
| Wan2.2-Native | passthrough | passthrough | ✓ built | — |
| Wan2.2-Wrapper | passthrough | passthrough | — | ✓ built |
| Flux2/Klein | ✓ patched | ✓ patched | — | — |
| Qwen | ✓ patched | ✓ patched | — | — |
| Z-Image | ✓ patched | ✓ patched | — | — |
The audio_enabled flag controls whether audio-related weight keys are included. When audio_enabled=False, keys containing these strings are stripped: audio, vocoder, speech, audio_stream, cross_modal, video_to_audio, av_ca.
This replicates the behaviour of the abandoned LTX2MasterLoaderLD node, now per-LoRA with V3 API.
Use this flow when preparing LoRA training captions:
[Dataset Captioner]
|
v
[Dataset Caption Editor] <- optional post-processing (trigger word, find/replace)
|
v
[Dataset Caption Viewer] <- review/edit/re-caption individual images
|
v
[Dataset Export Summary] <- verify coverage and caption statistics
Then feed your dataset directory into your training configuration.
Dataset Captioner supports:
input_directory, output_directory, recursivecaptioner_type: qwen_vl (recommended), qwen_omni, or gemini_flashinstruction, trigger_word, clean_captiondevice: auto, cuda, or cpuuse_8bit (requires bitsandbytes)override_existing, unload_after, gemini_api_keyOutputs: dataset_path, caption_count, failed_count
Dataset Caption Editor runs in dry-run mode by default (dry_run=true) and only writes changes when disabled.
Dataset Caption Viewer provides thumbnail rows, caption editing, per-image re-caption, and clear-caption actions.
The current viewer table viewport is intentionally fixed-height for layout stability; the table scrolls internally.
Dataset Export Summary reports total/captioned/missing counts and caption length stats; set export_csv=true to write dataset_summary.csv.
Use scripts/dataset_caption_edit.fish for repeatable multi-pass find/replace edits against the /fbtools/dataset_caption/edit API.
# Dry-run (default)
fish scripts/dataset_caption_edit.fish --dataset rara \
--pass 'old phrase=>new phrase' \
--pass 'another old=>another new'
# Apply changes
fish scripts/dataset_caption_edit.fish --dataset rara --apply \
--pass 'old phrase=>new phrase' \
--pass 'another old=>another new'
Notes:
find=>replace.find_text and replace_text fields expected by the API.--output <dir> when captions are stored in a separate output directory.| Model | Precision | Approx VRAM |
|---|---|---|
| Qwen2.5-VL-7B | bf16 | ~16 GB |
| Qwen2.5-VL-7B | 8-bit | ~8 GB |
| Qwen2.5-Omni-7B | bf16 | ~20 GB |
| Qwen2.5-Omni-7B | 8-bit | ~11 GB |
| Gemini Flash | cloud | 0 GB |
trigger_word in Dataset Captioner instead of relying on prompt wording for consistency.Dataset Caption Viewer to correct hallucinations before training.Dataset Export Summary to validate.To install development dependencies and pre-commit hooks:
cd comfyui-fbTools
pip install -e .[dev]
pre-commit install
The -e flag installs in "editable" mode, so changes are immediately reflected when ComfyUI restarts.
comfyui-fbTools/
├── extension.py # Main Python extension with all nodes
├── prompt_models.py # Data models (PromptMetadata, PromptCollection)
├── utils/ # Python utilities
│ ├── io.py # File I/O operations
│ ├── util.py # General utilities
│ ├── pose.py # Pose detection utilities
│ └── images.py # Image processing utilities
├── js/ # JavaScript frontend code
│ ├── fb_tools.js # Main extension registration
│ ├── api/ # REST API clients
│ │ ├── libber.js # Libber API
│ │ ├── prompt_collection.js # PromptCollection API
│ │ ├── scene.js # Scene API
│ │ └── story.js # Story API
│ ├── nodes/ # Node-specific handlers
│ │ ├── libber.js # LibberManager & LibberApply
│ │ ├── scene.js # SceneSelect handler
│ │ └── story.js # StoryEdit & StoryView handlers
│ └── utils/ # Shared JavaScript utilities
├── tests/ # Python unit tests
│ ├── test_prompt_collection.py
│ └── test_libber.py
└── js-tests/ # JavaScript unit tests
├── prompt_collection_api.test.js
└── libber_api.test.js
# Run all tests
python -m pytest tests/ -v
# Run specific test file
python -m pytest tests/test_libber.py -v
# With coverage report
python -m pytest tests/ --cov=. --cov-report=html
Test Coverage:
See Testing Guide for detailed instructions.
cd js/
npm install # First time only
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage
Test Coverage:
See TEST_RESULTS.md for detailed test coverage.
The project uses:
See Frontend Documentation for architecture details.
If you wish to share this extension:
pyproject.tomlREGISTRY_ACCESS_TOKEN to GitHub SecretsSee ComfyUI Registry docs for details.
See LICENSE file.
See CHANGELOG.md for version history and release notes.
2026-04-09: LoRA Scene Nodes
LoraEntryDefine — define one LoRA for a specific model target with per-LoRA audio guard, enable toggle, and separate model/clip strengthsLoraStackCollect — collect up to 20 LORA_ENTRY inputs into a persisted JSON stack with optional merge from existing JSONLoraStackApply — apply persisted stack at inference time; routes to direct patching (LTX2.3, Flux, Qwen, Z-Image), LORA_STACK (Wan2.2-Native), or WANVIDLORA (Wan2.2-Wrapper)LORA_ENTRY and LORA_STACK_DATA types for type-safe wiringLTX2MasterLoaderLD with V3 API per-LoRA granularity2026-04-08: Subject Compositor Nodes
SubjectLayerDefine — define a subject layer with fractional padding, canvas offset, and optional background removalSubjectCompositor — composite 1–20 layers onto a canvas (composite, individual, or both output modes)SUBJECT_LAYER type wiring between the two nodesdivisible_by, default 32 for LTX/video models)2025-01-18: Generic Mask System
2024-12-19: Libber System Overhaul
Previous Updates:
296 commits
Python
63.0%
JavaScript
33.4%
CSS
3.5%