80 Commits
Author SHA1 Message Date
Vito Sansevero 0cd25a1073 test(model_resolution_picker): cover an override step off the latent grid being ignored whole
- A step of 3 in a user override must drop the whole file with the panel warning and leave the shipped SDXL step at 64.
2026-10-27 15:01:55 -07:00
Vito Sansevero ef5c9bb85a fix(model_resolution_picker): require family steps to be multiples of 8
- An override family could declare a step such as 3; sizes rounded to it
  are not divisible by 8, so create_empty_latent_batch raised ValueError
  at execution instead of producing a latent (Codex review on PR #108).
- validate_table now rejects a step that is not a multiple of 8 with a
  message naming the latent grid; the shipped steps (16, 32, 64) already
  comply, and the schema docs state the rule.
2026-10-27 15:01:19 -07:00
Vito Sansevero 959c6b9e9f style(model_resolution_picker): format the override tests 2026-10-27 12:50:43 -07:00
Vito Sansevero 729652facf fix(model_resolution_picker): include file size in the override cache key
- a replacement with the same mtime but different size was served stale
2026-10-27 12:49:17 -07:00
Vito Sansevero 47e95f95c3 fix(model_resolution_picker): bound sizes to the width/height widget limits
- table validation accepts sides 64..8192, matching the widget min/max
- parse_size_label (Python and JS) caps digits and rejects out-of-range
  sizes so create() falls back to width/height instead of overflowing
2026-10-27 12:49:10 -07:00
Vito Sansevero 623915fd7d fix(model_resolution_picker): feed override families into the model combo
- family_labels() and the combo default now read the merged table, so an
  appended family is selectable and a removed one is not offered
- an alias used as the override default is normalised to its label
- the frontend rebuilds the model options from the payload and keeps a
  saved family that is no longer listed so old workflows still show it
2026-10-27 12:48:42 -07:00
Vito Sansevero d55bc72c34 fix(model_resolution_picker): reset a stale resolution label and use binary megapixels
- Set resolution to custom whenever the panel has no selection (wired,
  failed, non-finite or loading) so a stale label cannot override width/height.
- Compute label megapixels with 1 MP = 1,048,576 px, matching the panel
  header, with half-up rounding so Python and JS labels agree on ties.
2026-10-27 12:31:09 -07:00
Vito Sansevero eaf03874b3 feat(model_resolution_picker): add a resolution dropdown synced with the chip grid
- Add a native resolution COMBO (shipped sizes only) so a size can be picked
  and saved in the workflow without the panel; custom or unparseable labels
  fall back to width/height on both sides.
- Mirror the dropdown with the chips: options follow the active chip, the
  value tracks the current size, and picking a label writes width/height.
- Draw a decorative ratio shape on each ratio chip and show the first
  families warning in the status line.
2026-10-27 12:22:16 -07:00
Vito Sansevero 211d69e04e fix(model_resolution_picker): ignore an override that nests too deeply to parse
- catch RecursionError alongside ValueError when loading, merging and validating
- add tests for deep nesting, remove vs families, and a default naming a removed family
2026-10-27 12:03:04 -07:00
Vito Sansevero cd6d0d9650 feat(model_resolution_picker): merge a user override file into the family table
- add override.py: override_path, merge_override, mtime-cached current_table
- resolve_family and families_payload consult the merged table and expose warnings
- an invalid override is ignored whole with a logged warning
2026-10-27 12:01:20 -07:00
Vito Sansevero 469d3740b8 fix(model_resolution_picker): stop huge integers from overflowing the table validator
- treat ints as finite instead of calling math.isfinite on them
- bound step (4096), megapixels (64) and side length (65536) with normal problem messages
- cover step, megapixels, default and size values of 10**400
2026-10-27 11:58:11 -07:00
Vito Sansevero cb3027717e fix(model_resolution_picker): keep the table validator from raising on malformed data
- check default, labels and aliases are strings before set membership
- reject non-finite step and megapixels via a shared _is_step helper
- cover unhashable and Infinity/NaN inputs in the garbage test
2026-10-27 11:56:54 -07:00
Vito Sansevero db64038b25 refactor(model_resolution_picker): move the family table to families.json with a validator
- ship the table as families.json with per-family notes from the old source comments
- add table.py: load_table, pure validate_table and build_families
- families.py loads and validates at import, raising RuntimeError on problems
- include *.json as package data
2026-10-27 11:54:19 -07:00
Vito Sansevero 38f820c8f5 fix(model_resolution_picker): accept renamed labels, dedupe chip sizes, apply early model change
- VALIDATE_INPUTS lets unknown family labels reach create()
- no size is listed under two chips; SD 1.5 3:4 gets true 3:4 sizes
- model change before the families payload loads is applied on load
- custom ratio: clear error on chip click, cap sides at 8192, accept X
- real-host assertions for model change and wired-in state
2026-10-27 11:02:45 -07:00
Vito Sansevero 83ed5ae42d feat(model_resolution_picker): add node, families route and registration
- add ModelResolutionPickerNode rounding to the family step
- serve GET /kikotools/model_resolution/families
- register the node in NODE_CLASS_MAPPINGS
2026-10-27 10:13:07 -07:00
Vito Sansevero f916636c6a feat(model_resolution_picker): add the model family resolution table
- add CHIPS, MIRROR, Family and the FAMILIES literal table
- add family_labels, resolve_family and families_payload helpers
- cover step, ratio, mirror and sort invariants with tests
2026-10-27 10:09:50 -07:00
Vito Sansevero 2f497cec5a feat(model_resolution_picker): add step rounding and size generation
- add round_to_step, generate_size and ratio_label pure helpers
- cover nearest-step rounding, minimum clamp, size math and aliases
2026-10-27 10:06:50 -07:00
Vito Sansevero 297f2b1863 fix: mark EmptyLatentBatch latents with their 8x spatial ratio
ComfyUI resizes an empty latent to the model's latent format only when the
latent carries downscale_ratio_spacial, as core EmptyLatentImage sets it.
Without it, models with 16x VAEs (Flux.2, Klein, MOSS-Image, Qwen-Image 2.1)
sampled at twice the requested width and height.
2026-10-26 20:07:03 -07:00
Vito Sansevero 1e86b73168 chore(gemini): soft-deprecate legacy prompt node 2026-10-16 15:44:31 -07:00
Vito Sansevero 64f70b6970 fix(model_compare_sheet): hide staging files from output image discovery 2026-10-16 09:03:56 -07:00
Vito Sansevero ae0fb7f229 fix(model_compare_sheet): replace rerun tiles without retaining stale batches 2026-10-16 08:59:29 -07:00
Vito Sansevero 335cc53961 fix(model_compare_sheet): reject run_name segments on both separators
- Split run_name on "/" and "\" and reject empty, "." and ".." parts, so
  a Windows-style "..\foo" can no longer resolve outside model_compare
  and let the index-0 archive step rename an unrelated output folder
- Reject drive-letter names and require the resolved directory to sit
  strictly below output/model_compare after symlinks are followed
- Nested run names such as project/date/sweep still work
2026-10-16 08:11:17 -07:00
Vito Sansevero c5e00c130b feat(xyz_helpers): add Checkpoint Folder Iterator and Model Compare Sheet
- Checkpoint Folder Iterator loads one checkpoint per queued run from a
  folder (relative to models/checkpoints or absolute), so a whole folder
  can be swept with only one model in memory at a time
- Model Compare Sheet saves each run's image as a tile under
  output/model_compare/<run_name>/ and rebuilds a labeled contact sheet
  from disk every run, so the grid survives between independent prompts
- Starting a sweep at index 0 archives stale tiles to a timestamped folder
- Running past the last checkpoint raises a clear error instead of
  wrapping, so an over-queued batch never restarts and archives a
  finished sweep
- Checkpoints load from the scanned absolute path, so folders outside the
  configured roots work as the tooltip promises
- Extract the LoRA Folder Batch folder-scanning, natural-sort and regex
  filter helpers into kikotools/base/model_folders.py so both tools share
  one implementation
2026-10-16 08:08:00 -07:00
Vito Sansevero 49523e10de chore(gemini_prompt): migrate to google-genai SDK
- google-generativeai is deprecated and emits a FutureWarning at node
  load; google-genai is Google's supported replacement
- Image analysis now uses genai.Client().models.generate_content
  instead of the module-level configure/GenerativeModel API
- Model listing reads supported_actions, the new SDK's name for
  supported_generation_methods
- Install instructions, requirements.txt, and README updated to the
  new package name
2026-10-13 18:27:52 -07:00
Vito Sansevero 143a6c47f6 fix(project_file_path): label the output STRING like djz-nodes
Drop RETURN_NAMES so ComfyUI shows the output as STRING, matching the original Project File Path Generator. Links connect by slot index, so existing workflows are unaffected.
2026-10-13 07:38:33 -07:00
Vito Sansevero d65c11c76c feat(project_file_path): add Project File Path node
Builds a save prefix like MyProject/2026-10-13/images/image for a save
node's filename_prefix, replacing djz-nodes' Project File Path Generator
with a fresh implementation:

- Same inputs, root defaults to empty (prefixes are already relative to
  the output folder, so "output" nested as output/output/...)
- Optional date folder; IS_CHANGED tracks the date instead of forcing a
  re-run on every queue
- Components are split on / and \ with '..' segments dropped, so a path
  can't climb out of the output folder
- Only real file extensions are stripped (render_v1.5 keeps its dot)
- The separator option is applied last instead of being undone by
  normpath; an empty filename raises a clear error
2026-10-13 07:27:49 -07:00
Vito Sansevero c2d85d4ba3 test: compare normalized save subfolder (forward slashes on Windows) 2026-10-13 07:23:11 -07:00
Vito Sansevero 2a637322b9 test: fix Windows-only test failures
- Close PIL images and NamedTemporaryFile handles before deleting the
  files; Windows refuses to delete open files (WinError 32)
- Set USERPROFILE alongside HOME: expanduser ignores HOME on Windows
- Compare realpaths in the nested save-prefix test; Windows temp dirs
  use 8.3 short names (RUNNER~1)
2026-10-13 07:18:30 -07:00
Vito Sansevero 30fc6aa7a7 fix(local_image_loader): refuse filesystem roots and home as saved paths
save_paths is reachable without auth, so saving "/" made every media
file on the machine servable. Reject filesystem/drive roots, the home
directory and its ancestors (realpath-resolved, so symlinks count), and
ignore such entries already in config.json.
2026-10-13 06:56:49 -07:00
Vito Sansevero a055543d16 fix(model_downloader): never write into custom_nodes or save code files
ComfyUI registers custom_nodes in folder_names_and_paths, so treating
every registered folder as a download root let a queued prompt drop a
.py file there that runs on the next restart.

- Skip custom_nodes when collecting allowed roots
- save_response only writes model/data extensions (.safetensors, .gguf,
  .ckpt, .pth, .json, ...), so any future code-holding folder is still
  protected
2026-10-13 06:41:13 -07:00
Vito Sansevero 4b66592715 test: cover gemini, embedding autocomplete, batch prompts, presets and plot nodes 2026-10-13 06:38:20 -07:00
Vito Sansevero dc3fcf65e2 fix: save path escape, global RNG reseed and small node bugs
- KikoSaveImage: reject absolute or '..' filename_prefix directories that
  resolved outside the output folder
- Film grain: use a local torch.Generator instead of reseeding the global
  RNG; strength 0 no longer skips the toe adjustment
- Purge VRAM: IS_CHANGED returns NaN so caching never skips the purge
- Resolution calculator: clamp outputs to >= 8
- Plot sampler params: font size floor of 1 for images under 32px wide
- Gemini prompt: drop Image.fromarray mode= argument (removed in Pillow)
2026-10-13 06:38:20 -07:00
Vito Sansevero c8de8f26d6 fix(model_downloader): confine downloads to model dirs and harden fetches
- Reject filenames with path components; reduce server-derived names
  (Content-Disposition, CivitAI redirect, URL) to a safe basename
- save_path must resolve inside models_dir or a registered model folder
  (including extra_model_paths.yaml roots); relative paths resolve
  against ComfyUI's base path
- Only http/https for user URLs and redirects; drop the Authorization
  header on cross-host redirects
- Stream to <file>.part and rename on success, removing the partial file
  on any failure, so a dropped connection no longer leaves a truncated
  model that later runs treat as complete
- 60s timeout on every request; shared streaming loop in BaseDownloader
- Custom downloader honours Content-Disposition before the default name
2026-10-13 06:38:20 -07:00
Vito Sansevero 10027fcef9 fix(local_image_loader): confine file routes to saved roots and media types
/view served any readable file (e.g. ~/.ssh/id_rsa) to anyone who could
reach the server; the only guard was a '..' substring check.

- Resolve paths with realpath and require commonpath containment in the
  saved paths or ComfyUI input/output directories
- /view serves image/video/audio only, /thumbnail and load_media images
  only; the extension check applies to the resolved path, so symlinks
  can't smuggle other files
- /images 403s outside the roots; page/per_page validated and clamped
- /list_directories (folder picker) returns directory names only
- load_media refuses disallowed paths from a tampered selections.json
- Route handlers moved to module level so they are unit tested
- Update stale tests to the (image, info) return contract
2026-10-13 06:34:13 -07:00
Vito Sansevero 60ce078aed fix(xyz_helpers): raise on invalid sampler/LoRA input instead of hanging
- LoRA strength strings: step <= 0 looped forever and reversed ranges
  returned [] (ZeroDivisionError later); now raise ValueError, and cap
  generated values at 1000
- Flux sampler params: unparseable steps/guidance/shift/denoise/seed
  tokens raise with the field name instead of being dropped, and an
  empty LoRA folder raises instead of returning (None, [])
- Detect Flux schnell via unet_config guidance_embed (the ModelType.FLOW
  check never matched Flux)
- IS_CHANGED: re-run Flux params when a seed is '?'; LoRA folder batch
  hashes file list + mtimes instead of always re-executing
- LoRA folder scan uses commonpath containment across all lora roots
- Prompt separators must be whole lines; failed encodes raise
2026-10-13 06:33:17 -07:00
Vito Sansevero c69281e795 fix(batch_list_converter): preserve all latent dict keys during split/join
Previously split_latent_batch and join_latent_batch only handled the
"samples" key, silently dropping metadata like noise_mask or batch_index.
This broke inpaint and masked workflows after a round-trip conversion.
2026-02-09 17:11:01 -08:00
Vito Sansevero 3bf8391e88 feat(batch_list_converter): add batch/list conversion nodes for IMAGE and LATENT
Add 4 utility nodes for converting between batched tensors and lists,
eliminating the dependency on Impact Pack for this common operation:
- ImageBatchToImageList (OUTPUT_IS_LIST)
- ImageListToImageBatch (INPUT_IS_LIST)
- LatentBatchToLatentList (OUTPUT_IS_LIST)
- LatentListToLatentBatch (INPUT_IS_LIST)
2026-02-09 16:39:28 -08:00
Vito Sansevero d1a5282ca0 style: fix Black formatting issues
Remove extra blank line in local_image_loader/node.py and wrap long
tuple assertion in test_empty_latent_batch.py.
2026-02-09 16:26:08 -08:00
Vito Sansevero 8773c8f249 feat(latent): add batch_size as 4th output from EmptyLatentBatchNode
Exposes batch_size as an output so downstream nodes can reference it
directly without needing a separate input.
2026-02-09 16:22:03 -08:00
Vito Sansevero 0a1cbe4990 fix(seeds): reduce seed range from 64-bit to 32-bit across all nodes
JS Math.random() only has 53 bits of integer precision, making the
64-bit range produce non-uniform values. 32-bit (0xFFFFFFFF) is the
standard ComfyUI seed range and works correctly in both Python and JS.

Also removes the redundant mode input from SeedHistoryNode, relying on
ComfyUI's built-in control_after_generate widget instead.
2026-02-09 16:21:56 -08:00
Vito Sansevero 20270d9501 test: Add unit tests for KikoWorkflowTimerNode 2025-12-21 06:45:35 -08:00
Vito Sansevero b527635254 test: update return types in test_assertion 2025-12-17 07:02:08 -08:00
Vito Sansevero 6368582e1b feat: Add WidthHeightToVec2Node with tests 2025-12-17 06:59:11 -08:00
Vito Sansevero 5b57d4fc35 test: Add tests for image counter functionality 2025-10-07 07:21:09 -07:00
Vito Sansevero 0a6ee72748 fix: replace LatentBatch import with local implementation for V3 schema compatibility
Refs #43

ComfyUI is converting nodes_latent.py to V3 Schema on October 8th, which will
break direct imports of LatentBatch. This commit replaces the import with a
local implementation copied directly from ComfyUI source code.

Changes:
- Removed: from comfy_extras.nodes_latent import LatentBatch
- Added: Local batch_latents() and reshape_latent_to() functions
- Updated: latentbatch.batch() calls to use batch_latents()
- Added: torch and comfy.utils imports for tensor operations
- Added: Comprehensive unit tests for latent batching functionality

The local implementation is functionally identical to the original and ensures
the node will continue working after the V3 schema migration.

Test Coverage:
- 5 new tests in TestLatentBatchingFunctions class
- All 16 tests passing (11 existing + 5 new)
- Tests cover tensor operations, batch indexing, and reshape logic
2025-10-05 06:08:21 -07:00
Vito Sansevero 27431b3a92 test(model_downloader): add unit tests for model downloader 2025-10-05 05:39:00 -07:00
Vito Sansevero 67ef0a44d7 test(text_input): add unit tests for TextInputNode 2025-10-05 05:38:47 -07:00
Vito Sansevero 5ae7985bc2 test(batch_prompts): add unit tests for batch prompts 2025-08-27 09:32:09 -07:00
Vito Sansevero d2b30f0a78 feat: Add auto-batching support for large LoRA collections
- Add auto-batching functionality to split large LoRA collections into manageable chunks
- Implement batch_size parameter to control number of LoRAs per batch (default: 25)
- Add batch_index parameter to select which batch to process
- Include batch tracking metadata in LORA_PARAMS for visualization
- Display batch info in plot parameters when available
- Update lora_list output to show batch header when auto-batching is enabled
- Add comprehensive tests for batching functionality
- Update documentation with detailed auto-batching usage instructions

This feature prevents UI disconnection issues when processing large numbers of LoRAs
by allowing users to process them in smaller batches sequentially.
2025-08-16 17:27:59 -07:00
Vito Sansevero db7d0dc86e test: Add line break and spacing adjustments 2025-08-13 07:04:55 -07:00