UI→API conversion drops a record-valued widget (ImageCropV2.crop_region) and runs the node with its default

Abierto
#1,011 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@allenflux ya está trabajando en esto.

Desde el 16/10/2026.

  • #1016 de @allenflux — abierto

Evaluación

Dificultad
3/5
Tiempo estimado
Medio día
Aptitud para principiantes
66/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
cli, tooling

Línea de trabajo

Start in workflow_to_api.py at _collect_widget_inputs (around L1671), which hands the whole node to _absorb_dict_widget_values (around L1714) whenever any widget value is a dict. Run the repro from the issue against convert_ui_to_api and confirm crop_region comes back as the schema default. Done when the record in widgets_values lands on its socketless widget and the Power Lora path still works.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

comfy-cli 1.22.0 (comfy-mcp 0.10.0 on top of it), ComfyUI 0.38.x, Windows 11. Reproduced on main (workflow_to_api.py, _collect_widget_inputs at L1671 and _absorb_dict_widget_values at L1714).

What happens

A node whose widget stores a record in widgets_values loses that value in the UI→API conversion. ImageCropV2 stores its crop_region (type BOUNDING_BOX, socketless: true) as {"x", "y", "width", "height"}; the converted prompt carries the schema default {"x": 0, "y": 0, "width": 512, "height": 512} instead. The job runs, so nothing warns: in the bundled 3d_pixal3d_multi_views template the four crops all become the same 512×512 corner of the input picture, the four "views" fed to Pixal3DMultiViewConditioning are identical, and the generated mesh is a radially symmetric object instead of the pictured one. The same workflow queued from the frontend gives the right prompt.

Repro (self-contained: the two schemas as GET /object_info returns them on ComfyUI 0.38, the node as the frontend saves it)

from comfy_cli.workflow_to_api import convert_ui_to_api
object_info = {
  "LoadImage": {"input": {"required": {"image": [["house.png"], {"image_upload": True}]}}, "input_order": {"required": ["image"]},
                "output": ["IMAGE", "MASK"], "output_name": ["IMAGE", "MASK"]},
  "ImageCropV2": {"input": {"required": {"image": ["IMAGE", {}],
                   "crop_region": ["BOUNDING_BOX", {"default": {"x": 0, "y": 0, "width": 512, "height": 512}, "socketless": True, "component": "ImageCrop"}]}},
                  "input_order": {"required": ["image", "crop_region"]}, "output": ["IMAGE"], "output_name": ["IMAGE"]},
}
wf = {"nodes": [
  {"id": 364, "type": "LoadImage", "widgets_values": ["house.png", "image"], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [1]}]},
  {"id": 338, "type": "ImageCropV2",
   "inputs": [{"name": "image", "type": "IMAGE", "link": 1},
              {"name": "crop_region", "type": "BOUNDING_BOX", "widget": {"name": "crop_region"}, "link": None}],
   "widgets_values": [{"x": 0, "y": 0, "width": 1669, "height": 2688}, 0, 0, 512, 512]}],
  "links": [[1, 364, 0, 338, 0, "IMAGE"]], "version": 0.4}
print(convert_ui_to_api(wf, object_info)["338"]["inputs"]["crop_region"])

Observed: {'x': 0, 'y': 0, 'width': 512, 'height': 512}. Expected: {'x': 0, 'y': 0, 'width': 1669, 'height': 2688}. The trailing 0, 0, 512, 512 are the Custom-ratio sub-widgets the frontend also serializes; with them removed the result is the same.

Why

_collect_widget_inputs sends the whole node through _absorb_dict_widget_values as soon as any value in the list is a dict (L1671). That function only understands Power Lora entries (a dict with type or lora); every other record is skipped, the input is never set, and _collect_default_inputs fills it from the schema. _is_widget_input does give crop_region a slot (it is socketless), so this is not the missing-slot case of #893: the slot exists, the value is thrown away by the dict shortcut.

Same shape for any socketless input whose frontend widget serializes a record: CreateBoundingBoxes.last_incoming, CropByBBoxes.bboxes, SAM3_Detect.bboxes, DrawBBoxes.bboxes, VideoTrim.trim, VideoCrop.crop (VIDEO_EDIT), CurveEditor.curve, Painter.mask, ImageCompositor.compositor, ImageCompare.compare_view, ResolutionSelector.preview on this install.

Suggestion

Take the Power Lora shortcut only when the dicts actually look like Power Lora entries (type/lora keys), and run the normal schema walk otherwise, letting a record land on its widget like a scalar does. Pairing by name (#873) would also solve it.

Workaround we use

Rewriting the node's widgets_values as a dict ({"crop_region": {...}}) before conversion: the converter already honours that form and the prompt comes out as saved.

Lenguaje dominante
Python
Estrellas
1k
Forks
176
Merge medio
1 d 11 h
PR fusionados (30 d)
87

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de Comfy-Org/comfy-cli

Todos los issues de Comfy-Org/comfy-cli

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.