Support list inputs in image_inputs for batch execution
This commit is contained in:
+24
-4
@@ -1663,8 +1663,14 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
|
||||
extracted_seeds = _extract_seed_list(seed)
|
||||
is_seed_list = extracted_seeds is not None
|
||||
is_config_list = _is_list_arg(config)
|
||||
max_image_list_len = (
|
||||
max((len(v) for v in image_inputs.values() if _is_list_arg(v)), default=0)
|
||||
if isinstance(image_inputs, dict)
|
||||
else 0
|
||||
)
|
||||
is_image_list = max_image_list_len > 0
|
||||
|
||||
is_batch = is_prompt_list or is_video_list or is_seed_list or is_config_list
|
||||
is_batch = is_prompt_list or is_video_list or is_seed_list or is_config_list or is_image_list
|
||||
|
||||
if not is_batch:
|
||||
return cls._execute_single(
|
||||
@@ -1689,6 +1695,7 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
|
||||
len(video_list) if is_video_list else 1,
|
||||
len(seed_list) if is_seed_list else 1,
|
||||
len(config_list) if is_config_list else 1,
|
||||
max_image_list_len if is_image_list else 1,
|
||||
)
|
||||
|
||||
def _unwrap_scalar(val, default=None):
|
||||
@@ -1727,11 +1734,24 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
|
||||
img_size_val = str(_unwrap_scalar(image_size, "None"))
|
||||
resp_schema_val = _unwrap_scalar(response_schema, None)
|
||||
base_seed = int(seed_list[0]) if (is_seed_list and len(seed_list) > 0) else int(_unwrap_scalar(seed, 0))
|
||||
has_distinct_item_inputs = (is_prompt_list and len(prompt_list) > 1) or (is_video_list and len(video_list) > 1)
|
||||
has_distinct_item_inputs = (
|
||||
(is_prompt_list and len(prompt_list) > 1)
|
||||
or (is_video_list and len(video_list) > 1)
|
||||
or (max_image_list_len > 1)
|
||||
)
|
||||
|
||||
def make_item_kwargs(idx, prepared_imgs=None, precomp_vid=None):
|
||||
p = prompt_list[min(idx, len(prompt_list) - 1)] if prompt_list else ""
|
||||
v = video_list[min(idx, len(video_list) - 1)] if video_list else None
|
||||
if isinstance(image_inputs, dict):
|
||||
item_imgs = {}
|
||||
for k, val in image_inputs.items():
|
||||
if _is_list_arg(val) and val:
|
||||
item_imgs[k] = val[min(idx, len(val) - 1)]
|
||||
else:
|
||||
item_imgs[k] = val
|
||||
else:
|
||||
item_imgs = image_inputs
|
||||
|
||||
s = (base_seed + idx) if (base_seed != 0 and batch_count > 1) else base_seed
|
||||
fp_seed = base_seed if has_distinct_item_inputs else s
|
||||
@@ -1768,7 +1788,7 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
|
||||
"timeout_fallback_text": timeout_fb_val,
|
||||
"image_size": img_size_val,
|
||||
"video": v,
|
||||
"image_inputs": image_inputs,
|
||||
"image_inputs": item_imgs,
|
||||
"response_schema": resp_schema_val,
|
||||
"prepared_images": prepared_imgs,
|
||||
"precomputed_video": precomp_vid,
|
||||
@@ -1787,7 +1807,7 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
|
||||
|
||||
print(f"[INFO] Preparing sequential list execution for {batch_count} items (model: {model_val})")
|
||||
prepared_images = None
|
||||
if image_inputs:
|
||||
if image_inputs and max_image_list_len <= 1:
|
||||
try:
|
||||
prepared_images = cls._prepare_image_inputs(image_inputs)
|
||||
except Exception:
|
||||
|
||||
+24
-4
@@ -1549,8 +1549,14 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode):
|
||||
extracted_seeds = _extract_seed_list(seed)
|
||||
is_seed_list = extracted_seeds is not None
|
||||
is_config_list = _is_list_arg(config)
|
||||
max_image_list_len = (
|
||||
max((len(v) for v in image_inputs.values() if _is_list_arg(v)), default=0)
|
||||
if isinstance(image_inputs, dict)
|
||||
else 0
|
||||
)
|
||||
is_image_list = max_image_list_len > 0
|
||||
|
||||
is_batch = is_prompt_list or is_video_list or is_seed_list or is_config_list
|
||||
is_batch = is_prompt_list or is_video_list or is_seed_list or is_config_list or is_image_list
|
||||
|
||||
if not is_batch:
|
||||
return cls._execute_single(
|
||||
@@ -1576,6 +1582,7 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode):
|
||||
len(video_list) if is_video_list else 1,
|
||||
len(seed_list) if is_seed_list else 1,
|
||||
len(config_list) if is_config_list else 1,
|
||||
max_image_list_len if is_image_list else 1,
|
||||
)
|
||||
|
||||
def _unwrap_scalar(val, default=None):
|
||||
@@ -1616,11 +1623,24 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode):
|
||||
resp_schema_val = _unwrap_scalar(response_schema, None)
|
||||
is_prev_id_list = _is_list_arg(previous_interaction_id) and len(previous_interaction_id) > 1
|
||||
base_seed = int(seed_list[0]) if (is_seed_list and len(seed_list) > 0) else int(_unwrap_scalar(seed, 0))
|
||||
has_distinct_item_inputs = (is_prompt_list and len(prompt_list) > 1) or (is_video_list and len(video_list) > 1)
|
||||
has_distinct_item_inputs = (
|
||||
(is_prompt_list and len(prompt_list) > 1)
|
||||
or (is_video_list and len(video_list) > 1)
|
||||
or (max_image_list_len > 1)
|
||||
)
|
||||
|
||||
def make_item_kwargs(idx, chained_prev_id="", prepared_imgs=None, precomp_vid=None):
|
||||
p = prompt_list[min(idx, len(prompt_list) - 1)] if prompt_list else ""
|
||||
v = video_list[min(idx, len(video_list) - 1)] if video_list else None
|
||||
if isinstance(image_inputs, dict):
|
||||
item_imgs = {}
|
||||
for k, val in image_inputs.items():
|
||||
if _is_list_arg(val) and val:
|
||||
item_imgs[k] = val[min(idx, len(val) - 1)]
|
||||
else:
|
||||
item_imgs[k] = val
|
||||
else:
|
||||
item_imgs = image_inputs
|
||||
|
||||
s = (base_seed + idx) if (base_seed != 0 and batch_count > 1) else base_seed
|
||||
fp_seed = base_seed if has_distinct_item_inputs else s
|
||||
@@ -1667,7 +1687,7 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode):
|
||||
"store": eff_store,
|
||||
"previous_interaction_id": prev_id,
|
||||
"video": v,
|
||||
"image_inputs": image_inputs,
|
||||
"image_inputs": item_imgs,
|
||||
"response_schema": resp_schema_val,
|
||||
"prepared_images": prepared_imgs,
|
||||
"precomputed_video": precomp_vid,
|
||||
@@ -1688,7 +1708,7 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode):
|
||||
|
||||
print(f"[INFO] Preparing sequential list execution for {batch_count} items (model: {model_val})")
|
||||
prepared_images = None
|
||||
if image_inputs and model_val not in cls.IMAGE_MODELS:
|
||||
if image_inputs and max_image_list_len <= 1 and model_val not in cls.IMAGE_MODELS:
|
||||
try:
|
||||
prepared_images = cls._prepare_image_inputs(image_inputs)
|
||||
except Exception:
|
||||
|
||||
@@ -1791,18 +1791,21 @@ def test_gemini_batch_single_list_element(structured_client):
|
||||
assert len(structured_client.calls) == 1
|
||||
|
||||
|
||||
def test_gemini_batch_videos(structured_client):
|
||||
def test_gemini_batch_images_list(structured_client):
|
||||
structured_client.responses = [
|
||||
[("final", "Vid 1")],
|
||||
[("final", "Vid 2")],
|
||||
[("final", "Img Resp 1")],
|
||||
[("final", "Img Resp 2")],
|
||||
]
|
||||
args = _execute_kwargs(_config())
|
||||
args.update(
|
||||
prompt="Describe video",
|
||||
video=[{"video": None, "fps": 1}, {"video": None, "fps": 1}],
|
||||
prompt="Describe image",
|
||||
image_inputs={
|
||||
"image_1": [torch.full((1, 4, 4, 3), 0.1), torch.full((1, 4, 4, 3), 0.2)],
|
||||
"image_2": [torch.full((1, 4, 4, 3), 0.3), torch.full((1, 4, 4, 3), 0.4)],
|
||||
},
|
||||
)
|
||||
output = gemini_nodes.SSL_GeminiTextPrompt.execute(**args)
|
||||
|
||||
assert isinstance(output[0], list)
|
||||
assert output[0] == ["Vid 1", "Vid 2"]
|
||||
assert isinstance(output[0], list) and len(output[0]) == 2
|
||||
assert output[0] == ["Img Resp 1", "Img Resp 2"]
|
||||
assert len(structured_client.calls) == 2
|
||||
|
||||
Reference in New Issue
Block a user