Files
Andy RatsirarsonandClaude Sonnet 5 c24e649d1c Add Gemini image generation node and multi-image Omni Flash references
- New APIImageGenerate node (external_api/image): text-to-image and
  image-to-image via Gemini's Imagen (text-only) and Nano Banana /
  Nano Banana Pro / 2 (text and multi-reference image-to-image) models.
- Omni Flash video now accepts a batch of reference images (not just
  one), sent in order as separate references in the same call.
- Fix Veo/Omni resolution and duration handling: correct 720/1080/4K
  resolution options per model tier, drop the non-existent
  veo-3.1-fast-generate-001 id, note Google's 8s lock on 1080/4K.
- Fix RunwayML model list: gen4_image/gen4_image_turbo are image
  models, not video, so drop them from the video node's provider list
  (tracked as follow-up work to add them to the image node).
- Update README and tests accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Bwg5aRmgChpwrVnpNgoQm
2026-09-16 15:52:25 -07:00

132 lines
3.1 KiB
Python

"""Stubs the parts of `comfy_api.latest` our nodes touch.
`comfy_api` only exists inside a running ComfyUI install, so unit tests fake
just enough of its surface (io.Schema/Input/Output/NodeOutput, ComfyAPI,
InputImpl.VideoFromFile) to import and exercise src/nodes.py in isolation.
"""
import sys
import types
def _install_fake_comfy_api():
if "comfy_api.latest" in sys.modules:
return
class _InputSpec:
def __init__(self, name, **kwargs):
self.name = name
self.kwargs = kwargs
class _InputFactory:
@staticmethod
def Input(name, **kwargs):
return _InputSpec(name, **kwargs)
class _OutputSpec:
def __init__(self, name=None, **kwargs):
self.name = name
self.kwargs = kwargs
class _OutputFactory:
@staticmethod
def Output(name=None, **kwargs):
return _OutputSpec(name, **kwargs)
class String(_InputFactory):
pass
class Float(_InputFactory):
pass
class Int(_InputFactory):
pass
class Combo(_InputFactory):
pass
class Image(_InputFactory, _OutputFactory):
pass
class _DynamicComboOption:
def __init__(self, key, inputs):
self.key = key
self.inputs = inputs
class _DynamicComboInputSpec(_InputSpec):
def __init__(self, name, options=None, **kwargs):
super().__init__(name, options=options, **kwargs)
self.options = options or []
class DynamicCombo:
Option = _DynamicComboOption
@staticmethod
def Input(id, options=None, **kwargs):
return _DynamicComboInputSpec(id, options=options, **kwargs)
class Video(_OutputFactory):
pass
class Hidden:
unique_id = "unique_id"
class Schema:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class NodeOutput:
def __init__(self, *args):
self.args = args
class ComfyNode:
hidden = None
io = types.SimpleNamespace(
String=String,
Float=Float,
Int=Int,
Combo=Combo,
Image=Image,
DynamicCombo=DynamicCombo,
Video=Video,
Hidden=Hidden,
Schema=Schema,
NodeOutput=NodeOutput,
ComfyNode=ComfyNode,
)
class ComfyExtension:
async def get_node_list(self):
raise NotImplementedError
class _Execution:
async def set_progress(self, value, max_value, node_id=None, **kwargs):
return None
class ComfyAPI:
def __init__(self):
self.execution = _Execution()
class VideoFromFile:
def __init__(self, file_obj):
self.file_obj = file_obj
InputImpl = types.SimpleNamespace(VideoFromFile=VideoFromFile)
latest = types.SimpleNamespace(
io=io,
ComfyExtension=ComfyExtension,
ComfyAPI=ComfyAPI,
InputImpl=InputImpl,
)
comfy_api = types.ModuleType("comfy_api")
comfy_api.latest = latest
sys.modules["comfy_api"] = comfy_api
sys.modules["comfy_api.latest"] = latest
_install_fake_comfy_api()