Closes#220 on the ImageApi side. The app picker follows in FileViewer-React.
What changes
Slots in the workflow: LoRA loader nodes titled imageapi:lora-slot:<n>:high and :low, numbered 1.. without gaps, each slot with at least one high and one low node.
Picks fill the slots in order. Each pick sets the slot nodes' lora_name and scales their exported strength_model/strength_clip.
Unused slots run at 0 with their exported file, so the graph is never rewired.
A malformed slot (missing side, gap, bad role, no lora_name/strength) fails that template at startup.
Catalog (src/enhance/lora_catalog.rs):
Read live from ComfyUI's GET /object_info/LoraLoaderModelOnly (classic and COMBO shapes).
Cached 60 s. A submit naming an unknown id re-reads it once, so a new download is usable right away. An unreachable ComfyUI keeps the last list.
Files are paired by a High/Low name token: StyleHigh/StyleLow, x-HIGH/x-LOW, x_high_noise/x_low_noise.
A token counts at the start of the name, after a non-letter, or capitalised, so slow, Glowing and lower never pair.
Files in different folders never pair, and unpaired files aren't offered.
Excluded: every lora_name the workflow loads outside the slots (lightx2v and the switch LoRAs), plus [template.catalog] exclude substrings.
Ids and labels come from the file names, so they're stable, e.g. StyleHighWan2_2 becomes style-wan2-2 / "Style Wan2 2".
Config:[template.catalog] takes strength (default 1.0), min/max (0.0–1.5) and exclude, validated like the switches.
API:
GET /enhance/templates: each template gains slots: n and, when n > 0, catalog: {loras: [{id, label}], strength, min, max}.
POST /enhance: takes slots: [{lora, scale?}]. A missing scale means strength, <= 0 leaves the slot unused, other values are clamped. More picks than slots, duplicates, unknown ids, or picks on a template without slots are 400s.
Jobs: the resolved picks go in a new enhance_jobs.lora_slots column (JSON [{lora, label, scale}]; additive migration) and are returned as slots.
Docs:enhance.example.toml and the CLAUDE.md Enhance section.
Rolling out
The 4090's Animate workflow needs the slot nodes, two pairs chained after the Bounce switch. The export's lora_name must be a real file.
Restart ImageApi after the new export.
Until then nothing changes, since Animate has no slots. Once picks work, the Bounce switch is redundant: Bounce also shows up in the catalog.
Testing
Catalog: naming styles, unpaired and tokenless files, exclusions, folders, unique ids, both object_info shapes.
Templates: slot discovery and the fixed LoRA list; picks filled in order with an unused slot at 0; too many picks; malformed slots.
Config:[template.catalog] parsing, validation and resolution.
Integration (mock ComfyUI):
catalog listed only on the slot template, without lightx2v, the switch or unpaired files;
a pick filled into slot 1 with slot 2 at 0;
stored and returned on the job, with the default scale applied;
a newly added LoRA found by re-reading the list;
400s for unknown, duplicate, too many and wrong template.
cargo test: 1093 lib tests pass, plus every integration binary. Clippy is clean.
Unrelated hang: twice today a full cargo test run hung in an integration binary, caught once in tests/otel_log_correlation. It passes alone and on master. I'll file it separately.
Closes #220 on the ImageApi side. The app picker follows in FileViewer-React.
## What changes
**Slots in the workflow:** LoRA loader nodes titled `imageapi:lora-slot:<n>:high` and `:low`, numbered 1.. without gaps, each slot with at least one high and one low node.
- Picks fill the slots in order. Each pick sets the slot nodes' `lora_name` and scales their exported `strength_model`/`strength_clip`.
- Unused slots run at 0 with their exported file, so the graph is never rewired.
- A malformed slot (missing side, gap, bad role, no `lora_name`/strength) fails that template at startup.
**Catalog (`src/enhance/lora_catalog.rs`):**
- Read live from ComfyUI's `GET /object_info/LoraLoaderModelOnly` (classic and COMBO shapes).
- Cached 60 s. A submit naming an unknown id re-reads it once, so a new download is usable right away. An unreachable ComfyUI keeps the last list.
- Files are paired by a High/Low name token: `StyleHigh`/`StyleLow`, `x-HIGH`/`x-LOW`, `x_high_noise`/`x_low_noise`.
- A token counts at the start of the name, after a non-letter, or capitalised, so `slow`, `Glowing` and `lower` never pair.
- Files in different folders never pair, and unpaired files aren't offered.
- **Excluded:** every `lora_name` the workflow loads outside the slots (lightx2v and the switch LoRAs), plus `[template.catalog] exclude` substrings.
- Ids and labels come from the file names, so they're stable, e.g. `StyleHighWan2_2` becomes `style-wan2-2` / "Style Wan2 2".
**Config:** `[template.catalog]` takes `strength` (default 1.0), `min`/`max` (0.0–1.5) and `exclude`, validated like the switches.
**API:**
- `GET /enhance/templates`: each template gains `slots: n` and, when n > 0, `catalog: {loras: [{id, label}], strength, min, max}`.
- `POST /enhance`: takes `slots: [{lora, scale?}]`. A missing scale means `strength`, <= 0 leaves the slot unused, other values are clamped. More picks than slots, duplicates, unknown ids, or picks on a template without slots are 400s.
- **Jobs:** the resolved picks go in a new `enhance_jobs.lora_slots` column (JSON `[{lora, label, scale}]`; additive migration) and are returned as `slots`.
**Docs:** `enhance.example.toml` and the CLAUDE.md Enhance section.
## Rolling out
1. The 4090's Animate workflow needs the slot nodes, two pairs chained after the Bounce switch. The export's `lora_name` must be a real file.
2. Restart ImageApi after the new export.
Until then nothing changes, since Animate has no slots. Once picks work, the Bounce switch is redundant: Bounce also shows up in the catalog.
## Testing
- **Catalog:** naming styles, unpaired and tokenless files, exclusions, folders, unique ids, both `object_info` shapes.
- **Templates:** slot discovery and the fixed LoRA list; picks filled in order with an unused slot at 0; too many picks; malformed slots.
- **Config:** `[template.catalog]` parsing, validation and resolution.
- **Integration (mock ComfyUI):**
- catalog listed only on the slot template, without lightx2v, the switch or unpaired files;
- a pick filled into slot 1 with slot 2 at 0;
- stored and returned on the job, with the default scale applied;
- a newly added LoRA found by re-reading the list;
- 400s for unknown, duplicate, too many and wrong template.
- `cargo test`: 1093 lib tests pass, plus every integration binary. Clippy is clean.
- **Unrelated hang:** twice today a full `cargo test` run hung in an integration binary, caught once in `tests/otel_log_correlation`. It passes alone and on master. I'll file it separately.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Templates with `imageapi:lora-slot:<n>:high|low` nodes take LoRAs picked
per job from a catalog built live from ComfyUI.
- Catalog: ComfyUI's LoraLoaderModelOnly `lora_name` choices (cached
60 s, re-read when a submit names an unknown id), paired by High/Low
name tokens (StyleHigh/StyleLow, x-HIGH/x-LOW, x_high_noise/...). Files
the workflow already loads outside the slots (lightx2v, switches) and
`[template.catalog] exclude` substrings never show; ids derive from the
names so they stay stable.
- POST /enhance `slots: [{lora, scale?}]` fills the slots in order: each
pick sets the slot nodes' lora_name and scales their exported strength;
unused slots run at 0, so the graph is never rewired. More picks than
slots, duplicates or unknown ids are 400s.
- GET /enhance/templates gains `slots` and `catalog: {loras, strength,
min, max}`; jobs store and return the picks (new enhance_jobs.lora_slots
JSON column).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Some checks are pending
Core Repos/ImageApi/pipeline/pr-master
Required
Some required checks are missing.
You are not authorized to merge this pull request.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #220 on the ImageApi side. The app picker follows in FileViewer-React.
What changes
Slots in the workflow: LoRA loader nodes titled
imageapi:lora-slot:<n>:highand:low, numbered 1.. without gaps, each slot with at least one high and one low node.lora_nameand scales their exportedstrength_model/strength_clip.lora_name/strength) fails that template at startup.Catalog (
src/enhance/lora_catalog.rs):GET /object_info/LoraLoaderModelOnly(classic and COMBO shapes).StyleHigh/StyleLow,x-HIGH/x-LOW,x_high_noise/x_low_noise.slow,Glowingandlowernever pair.lora_namethe workflow loads outside the slots (lightx2v and the switch LoRAs), plus[template.catalog] excludesubstrings.StyleHighWan2_2becomesstyle-wan2-2/ "Style Wan2 2".Config:
[template.catalog]takesstrength(default 1.0),min/max(0.0–1.5) andexclude, validated like the switches.API:
GET /enhance/templates: each template gainsslots: nand, when n > 0,catalog: {loras: [{id, label}], strength, min, max}.POST /enhance: takesslots: [{lora, scale?}]. A missing scale meansstrength, <= 0 leaves the slot unused, other values are clamped. More picks than slots, duplicates, unknown ids, or picks on a template without slots are 400s.enhance_jobs.lora_slotscolumn (JSON[{lora, label, scale}]; additive migration) and are returned asslots.Docs:
enhance.example.tomland the CLAUDE.md Enhance section.Rolling out
lora_namemust be a real file.Until then nothing changes, since Animate has no slots. Once picks work, the Bounce switch is redundant: Bounce also shows up in the catalog.
Testing
object_infoshapes.[template.catalog]parsing, validation and resolution.cargo test: 1093 lib tests pass, plus every integration binary. Clippy is clean.cargo testrun hung in an integration binary, caught once intests/otel_log_correlation. It passes alone and on master. I'll file it separately.🤖 Generated with Claude Code
Templates with `imageapi:lora-slot:<n>:high|low` nodes take LoRAs picked per job from a catalog built live from ComfyUI. - Catalog: ComfyUI's LoraLoaderModelOnly `lora_name` choices (cached 60 s, re-read when a submit names an unknown id), paired by High/Low name tokens (StyleHigh/StyleLow, x-HIGH/x-LOW, x_high_noise/...). Files the workflow already loads outside the slots (lightx2v, switches) and `[template.catalog] exclude` substrings never show; ids derive from the names so they stay stable. - POST /enhance `slots: [{lora, scale?}]` fills the slots in order: each pick sets the slot nodes' lora_name and scales their exported strength; unused slots run at 0, so the graph is never rewired. More picks than slots, duplicates or unknown ids are 400s. - GET /enhance/templates gains `slots` and `catalog: {loras, strength, min, max}`; jobs store and return the picks (new enhance_jobs.lora_slots JSON column). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.