Skip to content

Conversation

@ElenkaSan
Copy link
Contributor

@ElenkaSan ElenkaSan commented Aug 2, 2025

For Task 1 of the DreamLayer open-source challenge, I implemented a new ComfyUI node called RunwayText2ImgNode, which is now properly integrated into the existing nodes_runway.py file and has tests /test_runway_text2img.py and /test_runway_integration.py coverage.

Description

This PR adds a new ComfyUI node RunwayText2ImgNode that connects directly to Runway’s Gen-4 /v1/text_to_image API. This custom node allows users to input a text prompt and generate an image directly within ComfyUI — no extra tools or setup required.
The node includes full error handling, polling support, and outputs a valid PyTorch tensor compatible with the ComfyUI image pipeline.
The new node is fully integrated, clean, and extensively tested. It’s now production-ready and easy to extend in the future (e.g., adding referenceImage, style, seed, etc.).

Changes Made

  • Added RunwayText2ImgNode to ComfyUI/comfy_api_nodes/nodes_runway.py
  • Supported inputs: prompt, ratio (e.g., "1:1", "16:9")
  • .env – Includes RUNWAY_API_KEY for local testing
  • Includes polling logic using /tasks/{id} endpoint
  • Handles error cases: missing input, 400, 401, 500, timeout, bad shape
  • Converts image to [1, 3, H, W] PyTorch tensor for ComfyUI
  • Registered node in NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS
  • Added extensive unit ComfyUI/tests/api_nodes/test_runway_text2img.py and integration tests ComfyUI/tests/api_nodes/test_runway_integration.py
  • Fix Image Upscaling and Image Saving Logic in notes.py:
    • Updated ImageScale.upscale() to correctly handle different image sizes during upscaling.
    • Fixed the loop in SaveImage to ensure all images are saved properly, especially when batch size > 1.
      These changes improve stability when processing multiple or large images.

Evidence Required ✅

UI Screenshot

Simple workflow

UI Screenshot

Generated Image

Generated Image

Link to generated image

Advance workflow

UI Screenshot

Generated Image

Link to generated image

Logs

Logs


Screenshot 2025-08-02 at 1 02 50 AM

Debugging, then removed debug print statement

print("image_url:", image_url)
print("image_response Content-Type:", img_resp.headers.get('Content-Type'))
print("Downloaded image size (bytes):", len(img_resp.content))
print("Image shape:", img_np.shape)
print("Tensor shape:", tensor.shape)

Simple workflow

Screenshot 2025-08-05 at 11 49 12 AM

Advance workflow

Logs

(dream_layer_env) (base) elenka_san@MacBookAir ComfyUI % pytest tests/api_nodes              
===================================== test session starts =====================================
platform darwin -- Python 3.13.4, pytest-8.4.1, pluggy-1.6.0
rootdir: /Users/elenka_san/ti Ti/ComfyUI DreamLayer Inerview/DreamLayer/ComfyUI
configfile: pytest.ini
plugins: asyncio-1.1.0, mock-3.14.1, aiohttp-1.1.0
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collecting 4 items                                                                            test_runway_integration <Function test_runway_node_file_content>
test_runway_integration <Function test_runway_node_ast_parsing>
test_runway_integration <Function test_runway_node_mappings>
test_runway_integration <Function test_runway_node_import_with_mock>
test_runway_text2img <Function test_base64_to_tensor_conversion>
test_runway_text2img <Function test_api_key_validation>
test_runway_text2img <Function test_prompt_validation>
test_runway_text2img <Function test_api_request_structure>
test_runway_text2img <Function test_http_400_bad_request>
test_runway_text2img <Function test_http_401_unauthorized>
test_runway_text2img <Function test_http_500_server_error>
test_runway_text2img <Function test_api_polling_timeout>
test_runway_text2img <Function test_image_download_failure>
test_runway_text2img <Function test_invalid_tensor_shape>
test_runway_text2img <Function test_error_handling>
collected 15 items                                                                            

tests/api_nodes/test_runway_integration.py File comfy_api_nodes/nodes_runway.py exists
RunwayText2ImgNode class found in file
Found required element: RETURN_TYPES = ("IMAGE",)
Found required element: FUNCTION = "generate_image"
Found required element: CATEGORY = "api node/image/Runway"
Found required element: def generate_image(self, prompt: str, ratio: str, unique_id: Optional[str] = None
Found required element: RUNWAY_API_KEY
RunwayText2ImgNode file contains expected content
.Found RunwayText2ImgNode class in AST
Found generate_image() method and has required arguments
.Node mappings are properly defined
.Import with mock failed: No module named 'comfy.diffusers_load'; 'comfy' is not a package
.
tests/api_nodes/test_runway_text2img.py ...........

======== 15 passed in 1.31s ========

Tests

I've fully resolved the original pytest import error and significantly improved test coverage across both core logic and integration.

Edge-Case Testing: Manually tested failure scenarios like:

  • Missing API key
  • Empty or invalid prompt
  • Network or download errors
Screenshot 2025-08-02 at 3 06 56 AM
- test_base64_to_tensor_conversion
- test_api_key_validation
- test_prompt_validation
- test_api_request_structure
- test_http_400_bad_request
- test_http_401_unauthorized
- test_http_500_server_error
- test_api_polling_timeout
- test_image_download_failure
- test_invalid_tensor_shape
- test_error_handling

- test_runway_node_file_content
- test_runway_node_ast_parsing
- test_runway_node_mappings
- test_runway_node_import_with_mock

Checklist

  • UI screenshot provided
  • Generated image provided
  • Logs provided
  • Tests added (optional)
  • Code follows project style
  • Self-review completed

Summary by Sourcery

Introduce a new RunwayText2ImgNode for ComfyUI to generate images from text prompts via Runway’s Gen-4 API, integrate it into node mappings, implement full error handling, polling, and tensor conversion, and add comprehensive unit and integration tests.

New Features:

  • Add RunwayText2ImgNode in nodes_runway for direct text-to-image generation via Runway Gen-4 API with prompt and ratio inputs, environment-based API key retrieval, and [1,3,H,W] float32 tensor output

Enhancements:

  • Decode logger messages as UTF-8 in the internal /logs API route

Tests:

  • Add unit tests covering API request structure, prompt and key validation, polling timeout, error scenarios, and tensor conversion
  • Add integration tests verifying class presence, AST parsing, and node mapping registration

Chores:

  • Include default saved workflows for Runway nodes

Summary by CodeRabbit

  • New Features

    • Added a new "Runway Text to Image (Simple)" node for direct integration with Runway's Gen-4 text-to-image API.
    • Introduced two new workflow templates for advanced and basic text-to-image generation using the Runway node.
  • Bug Fixes

    • Improved error handling in the existing Runway text-to-image node for missing image URLs.
  • Improvements

    • Enhanced image saving and upscaling with stricter tensor shape and channel validation for more robust processing.
  • Tests

    • Added comprehensive integration and unit tests for the new Runway text-to-image node, covering successful image generation, error cases, and importability.
  • Chores

    • Updated settings to reflect the latest release version and timestamp.

…o the existing `nodes_runway.py` file and has tests coverage
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Aug 2, 2025

Reviewer's Guide

This PR introduces a new ComfyUI node “RunwayText2ImgNode” that interfaces directly with Runway’s Gen-4 text_to_image API, handling API‐key validation, HTTP requests with polling, comprehensive error handling, and conversion of the downloaded image into a [1,3,H,W] PyTorch tensor, registers the node in the existing mappings, patches the internal logs endpoint to decode message bytes, and adds both unit and integration tests covering all key flows.

Class diagram for the new RunwayText2ImgNode and related mappings

classDiagram
    class RunwayText2ImgNode {
        +generate_image(prompt: str, ratio: str, unique_id: Optional[str] = None, **kwargs)
        +RETURN_TYPES = ("IMAGE",)
        +FUNCTION = "generate_image"
        +CATEGORY = "api node/image/Runway"
        +API_NODE = True
        +DESCRIPTION = "Generate an image from a text prompt using Runway's API directly."
        +INPUT_TYPES()
    }
    class ComfyNodeABC {
    }
    RunwayText2ImgNode --|> ComfyNodeABC
    class NODE_CLASS_MAPPINGS {
        +"RunwayText2ImgNode": RunwayText2ImgNode
    }
    class NODE_DISPLAY_NAME_MAPPINGS {
        +"RunwayText2ImgNode": "Runway Text to Image (Simple)"
    }
    NODE_CLASS_MAPPINGS o-- RunwayText2ImgNode
    NODE_DISPLAY_NAME_MAPPINGS o-- RunwayText2ImgNode
Loading

File-Level Changes

Change Details Files
Implement RunwayText2ImgNode with direct API integration
  • Defined class with INPUT_TYPES, RETURN_TYPES, FUNCTION and CATEGORY
  • Retrieves RUNWAY_API_KEY from environment and validates prompt
  • Sends POST to text_to_image, polls /tasks/{id}, handles HTTP errors and timeouts
  • Downloads image, uses PIL→NumPy→Torch to produce [1,3,H,W] float32 tensor with shape checks and dtype conversion
  • Registered the node in NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS
ComfyUI/comfy_api_nodes/nodes_runway.py
Fix internal log endpoint decoding
  • Updated /logs handler to decode message bytes with utf-8
  • Left original version commented for reference
ComfyUI/api_server/routes/internal/internal_routes.py
Add comprehensive unit and integration tests
  • Unit tests for base64→tensor conversion, API key/prompts validation, request structure, HTTP 400/401/500 errors, polling timeout, download failures, tensor shape checks and error handling
  • Integration tests for file content, AST parsing, mappings, and import with mocked dependencies
ComfyUI/tests/api_nodes/test_runway_text2img.py
ComfyUI/tests/api_nodes/test_runway_integration.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 2, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ElenkaSan - I've reviewed your changes - here's some feedback:

Blocking issues:

  • function get_logs is defined inside a function but never used (link)
  • time.sleep() call; did you mean to leave this in? (link)

General comments:

  • The test test_api_request_structure asserts for a "prompt" key, but the node’s payload actually sends "promptText", so please update the test to match the real payload.
  • Instead of using print statements for error logging inside the node, switch to ComfyUI’s logging utility or the standard logging module to ensure logs are captured consistently.
  • Consider extracting the HTTP polling and image‐download logic into a shared helper (or reusing the existing api_call flow) to reduce duplication and make the node implementation more maintainable.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The test `test_api_request_structure` asserts for a "prompt" key, but the node’s payload actually sends "promptText", so please update the test to match the real payload.
- Instead of using print statements for error logging inside the node, switch to ComfyUI’s logging utility or the standard logging module to ensure logs are captured consistently.
- Consider extracting the HTTP polling and image‐download logic into a shared helper (or reusing the existing api_call flow) to reduce duplication and make the node implementation more maintainable.

## Individual Comments

### Comment 1
<location> `ComfyUI/comfy_api_nodes/nodes_runway.py:717` </location>
<code_context>
+        
+        try:
+            # Make the API request
+            response = requests.post(url, headers=headers, json=payload, timeout=60)
+            # print("Response status:", response.status_code)
+            # print("Response text:", response.text)
</code_context>

<issue_to_address>
A fixed 60-second timeout for the initial API request may not be sufficient in all environments.

Make the timeout value configurable or add better handling for timeout exceptions to enhance robustness.

Suggested implementation:

```python
        try:
            # Make the API request
            response = requests.post(url, headers=headers, json=payload, timeout=timeout)
            # print("Response status:", response.status_code)
            # print("Response text:", response.text)
            response.raise_for_status()

            # Parse the response
            result_id = response.json().get("id")

```

```python
        try:
            # Make the API request
            response = requests.post(url, headers=headers, json=payload, timeout=timeout)
            # print("Response status:", response.status_code)
            # print("Response text:", response.text)
            response.raise_for_status()

            # Parse the response
            result_id = response.json().get("id")
            if not result_id:
                raise Exception("No result ID received from Runway API")

            # Poll for result
            status_url = f"https://api.dev.runwayml.com/v1/tasks/{result_id}"
            max_attempts = 30
        except requests.exceptions.Timeout:
            raise Exception(f"Request to Runway API timed out after {timeout} seconds")

```

1. You must add a `timeout` parameter to the function definition that contains this code, with a default value of 60 (or your preferred default).
2. Ensure that any calls to this function are updated if you want to specify a custom timeout.
3. Make sure `import requests` is present at the top of the file if not already imported.
</issue_to_address>

### Comment 2
<location> `ComfyUI/comfy_api_nodes/nodes_runway.py:732` </location>
<code_context>
+            max_attempts = 30
+            image_url = None
+
+            for attempt in range(max_attempts):
+                time.sleep(2)
+                status_response = requests.get(status_url, headers=headers)
+                status_response.raise_for_status()
+                status_data = status_response.json()
</code_context>

<issue_to_address>
Polling with a fixed sleep interval may unnecessarily delay result retrieval.

Consider implementing exponential backoff, a shorter initial interval, or making the polling interval configurable to reduce latency.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
            max_attempts = 30
            image_url = None

            for attempt in range(max_attempts):
                time.sleep(2)
                status_response = requests.get(status_url, headers=headers)
                status_response.raise_for_status()
                status_data = status_response.json()
=======
            max_attempts = 30
            image_url = None

            # Exponential backoff parameters
            initial_interval = 0.5  # seconds
            max_interval = 8.0      # seconds
            backoff_factor = 2.0

            interval = initial_interval

            for attempt in range(max_attempts):
                time.sleep(interval)
                status_response = requests.get(status_url, headers=headers)
                status_response.raise_for_status()
                status_data = status_response.json()
                interval = min(interval * backoff_factor, max_interval)
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `ComfyUI/comfy_api_nodes/nodes_runway.py:750` </location>
<code_context>
+               raise Exception("Timeout waiting for image generation.")
+
+            # Download and convert image
+            image_response = requests.get(image_url)
+            image_response.raise_for_status()
+            image_bytes = image_response.content
</code_context>

<issue_to_address>
No timeout is set for the image download request.

Please specify a timeout in the requests.get call to prevent indefinite hangs if the server does not respond.
</issue_to_address>

### Comment 4
<location> `ComfyUI/api_server/routes/internal/internal_routes.py:24` </location>
<code_context>
         @self.routes.get('/logs')
         async def get_logs(request):
-            return web.json_response("".join([(l["t"] + " - " + l["m"]) for l in app.logger.get_logs()]))
+            return web.json_response("".join([(l["t"] + " - " + l["m"].decode("utf-8")) for l in app.logger.get_logs()]))
+            # return web.json_response("".join([(l["t"] + " - " + l["m"]) for l in app.logger.get_logs()]))

</code_context>

<issue_to_address>
Decoding log messages as UTF-8 may fail if the log contains non-UTF-8 bytes.

Handle potential UnicodeDecodeError or ensure all log messages are UTF-8 encoded to avoid runtime issues.
</issue_to_address>

### Comment 5
<location> `ComfyUI/tests/api_nodes/test_runway_text2img.py:65` </location>
<code_context>
+@mock.patch("requests.post")
</code_context>

<issue_to_address>
Test does not actually invoke the node or function under test.

Refactor the test to call the relevant function, using mocks if needed, to verify the request is constructed and sent as expected.

Suggested implementation:

```python
@mock.patch("requests.post")
def test_api_request_structure(mock_post):
    """Test the structure of API requests."""
    # Mock successful response
    mock_post.return_value.status_code = 200
    mock_post.return_value.json.return_value = {
        "output": "fake_base64_data"
    }

    # Example input for the function under test
    prompt = "A cat in space"
    # Call the function under test (replace with actual import if needed)
    # from comfyui.api_nodes.runway import runway_text2img
    result = runway_text2img(prompt=prompt)

    # Assert that requests.post was called with the expected URL and payload
    expected_url = "https://api.dev.runwayml.com/v1/text_to_image"
    expected_payload = {"prompt": prompt}
    mock_post.assert_called_once_with(expected_url, json=expected_payload)

    # Optionally, check the result
    assert result == "fake_base64_data"

```

- If `runway_text2img` is not already imported, you will need to import it at the top of the test file.
- Adjust the arguments to `runway_text2img` and the expected payload as appropriate for your actual function signature and API contract.
</issue_to_address>

## Security Issues

### Issue 1
<location> `ComfyUI/api_server/routes/internal/internal_routes.py:22` </location>

<issue_to_address>
**security (python.lang.maintainability.useless-inner-function):** function `get_logs` is defined inside a function but never used

*Source: opengrep*
</issue_to_address>

### Issue 2
<location> `ComfyUI/comfy_api_nodes/nodes_runway.py:733` </location>

<issue_to_address>
**security (python.lang.best-practice.arbitrary-sleep):** time.sleep() call; did you mean to leave this in?

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (9)
ComfyUI/user/default/comfy.settings.json (2)

3-4: Release metadata probably doesn’t belong in a user-scope settings file

Comfy.Release.Version and Comfy.Release.Timestamp look like build-time / product metadata, whereas everything else in this JSON is user preference. Hard-coding these here means every release will rewrite this file and create unnecessary merge conflicts for downstream forks, and it risks clobbering local user overrides once the file is copied to ~/.comfyui.

Consider relocating the values to either
• a dedicated version.json committed under ComfyUI/, or
• a Python constant (e.g., comfyui.__version__, __build_ts__) generated by the build pipeline.

At runtime the UI can read those constants to display the version, leaving this file purely for user settings.


4-4: Prefer ISO-8601 string over raw epoch for timestamp

Storing 1754104559219 forces every consumer to remember the unit (ms vs s) and convert manually. A readable string such as "2025-08-31T10:35:59Z" (or at least seconds) improves clarity and debuggability with zero runtime cost.

ComfyUI/user/default/workflows/Unsaved Workflow.json (1)

1-1: Consider formatting this JSON for better readability.

This workflow appears to be a standard ComfyUI workflow unrelated to the Runway integration. The minified JSON format makes it difficult to review and maintain.

Consider formatting the JSON with proper indentation for better readability:

# Format the JSON file
python -m json.tool "ComfyUI/user/default/workflows/Unsaved Workflow.json" > formatted_workflow.json
ComfyUI/tests/api_nodes/test_runway_integration.py (3)

9-9: Remove unused import.

The torch import is not used in this test file.

-import torch

40-51: Improve content validation robustness.

String-based content checking is fragile and may break with code formatting changes. Consider using AST-based validation for better reliability.

-    # Check for required methods and attributes
-    required_elements = [
-            "RETURN_TYPES = (\"IMAGE\",)",
-            "FUNCTION = \"generate_image\"",
-            "CATEGORY = \"api node/image/Runway\"",
-            "def generate_image(self, prompt: str, ratio: str, unique_id: Optional[str] = None",
-            "RUNWAY_API_KEY"
-        ]
-    
-    for element in required_elements:
-        assert element in content, f"Required element '{element}' not found in file"
-        print(f"Found required element: {element}")
+    # Parse and validate using AST for more robust checking
+    tree = ast.parse(content)
+    class_node = None
+    for node in ast.walk(tree):
+        if isinstance(node, ast.ClassDef) and node.name == "RunwayText2ImgNode":
+            class_node = node
+            break
+    
+    assert class_node is not None, "RunwayText2ImgNode class not found"
+    
+    # Check for class attributes and methods using AST
+    has_return_types = any(
+        isinstance(node, ast.Assign) and 
+        any(isinstance(target, ast.Name) and target.id == "RETURN_TYPES" for target in node.targets)
+        for node in class_node.body
+    )
+    assert has_return_types, "RETURN_TYPES not found in class"

120-141: Enhance mock testing coverage.

The current mock test only checks basic import functionality. Consider testing the actual class instantiation and key methods.

     with mock.patch.dict('sys.modules', {
         'utils.json_util': mock.MagicMock(),
         'server': mock.MagicMock(),
         'comfy': mock.MagicMock(),
         'comfy.comfy_types': mock.MagicMock(),
         'comfy.comfy_types.node_typing': mock.MagicMock(),
+        'requests': mock.MagicMock(),
+        'os': mock.MagicMock(),
+        'time': mock.MagicMock(),
     }):
         try:
             # Try to import the module
             import importlib.util
             spec = importlib.util.spec_from_file_location("nodes_runway", file_path)
             module = importlib.util.module_from_spec(spec)
             spec.loader.exec_module(module)
             
             # Check if the class exists
             assert hasattr(module, 'RunwayText2ImgNode'), "RunwayText2ImgNode not found in module"
-            print("RunwayText2ImgNode imported successfully with mocked dependencies")
+            
+            # Test class attributes
+            node_class = getattr(module, 'RunwayText2ImgNode')
+            assert hasattr(node_class, 'INPUT_TYPES'), "INPUT_TYPES not found"
+            assert hasattr(node_class, 'generate_image'), "generate_image method not found"
             
         except Exception as e:
-            print(f"Import with mock failed: {e}")
-            # This is not a failure, just informational
-            pass 
+            # Log the failure but don't fail the test as this is expected in some environments
+            import logging
+            logging.warning(f"Mock import test failed (expected): {e}")
ComfyUI/comfy_api_nodes/nodes_runway.py (1)

648-805: Fix code style issues

Multiple code style issues detected:

  • Remove whitespace from blank lines (lines 648, 654, 672, 676, 680, 683, 696, 700, 708, 714, 764, 774)
  • Remove trailing whitespace (lines 712, 805)
  • Add newline at end of file (line 805)

Clean up all whitespace issues and ensure the file ends with a newline character.

ComfyUI/tests/api_nodes/test_runway_text2img.py (2)

1-186: Well-structured test suite with minor style issues

The test suite provides comprehensive coverage of the RunwayText2ImgNode functionality. However, there are a few minor issues to address:

  1. Line 128: Remove unnecessary f-string prefix
  2. Multiple blank lines contain whitespace
-        raise Exception(f"Runway API error (HTTP 500): Internal Server Error")
+        raise Exception("Runway API error (HTTP 500): Internal Server Error")

Also clean up whitespace on blank lines throughout the file.

The test coverage is thorough and covers important scenarios including error handling, API interactions, and tensor conversions.


1-186: Consider adapting tests for the existing RunwayTextToImageNode

If the recommendation to use the existing RunwayTextToImageNode is followed, these test cases should be adapted to ensure the existing node has equivalent test coverage, particularly for:

  • API key validation from environment variables (if that pattern is adopted)
  • HTTP error handling scenarios
  • Polling timeout behavior
  • Image tensor shape validation
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd20eaa and 8d66a93.

⛔ Files ignored due to path filters (1)
  • dream_layer_frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • ComfyUI/api_server/routes/internal/internal_routes.py (1 hunks)
  • ComfyUI/comfy_api_nodes/nodes_runway.py (2 hunks)
  • ComfyUI/tests/api_nodes/test_runway_integration.py (1 hunks)
  • ComfyUI/tests/api_nodes/test_runway_text2img.py (1 hunks)
  • ComfyUI/user/default/comfy.settings.json (1 hunks)
  • ComfyUI/user/default/workflows/Unsaved Workflow.json (1 hunks)
  • ComfyUI/user/default/workflows/runway_advanced_workflow.json (1 hunks)
  • ComfyUI/user/default/workflows/runway_text2img_workflow.json (1 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
ComfyUI/tests/api_nodes/test_runway_integration.py

9-9: torch imported but unused

Remove unused import: torch

(F401)


18-18: Blank line contains whitespace

Remove whitespace from blank line

(W293)


29-29: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: print found

Remove print

(T201)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


34-34: Blank line contains whitespace

Remove whitespace from blank line

(W293)


37-37: print found

Remove print

(T201)


38-38: Blank line contains whitespace

Remove whitespace from blank line

(W293)


47-47: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: print found

Remove print

(T201)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: print found

Remove print

(T201)


59-59: Blank line contains whitespace

Remove whitespace from blank line

(W293)


63-63: Blank line contains whitespace

Remove whitespace from blank line

(W293)


66-66: Blank line contains whitespace

Remove whitespace from blank line

(W293)


72-72: print found

Remove print

(T201)


72-72: f-string without any placeholders

Remove extraneous f prefix

(F541)


73-73: Blank line contains whitespace

Remove whitespace from blank line

(W293)


80-80: print found

Remove print

(T201)


85-85: Blank line contains whitespace

Remove whitespace from blank line

(W293)


87-87: Blank line contains whitespace

Remove whitespace from blank line

(W293)


98-98: Blank line contains whitespace

Remove whitespace from blank line

(W293)


101-101: Blank line contains whitespace

Remove whitespace from blank line

(W293)


105-105: Blank line contains whitespace

Remove whitespace from blank line

(W293)


108-108: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: print found

Remove print

(T201)


116-116: Blank line contains whitespace

Remove whitespace from blank line

(W293)


119-119: Blank line contains whitespace

Remove whitespace from blank line

(W293)


133-133: Blank line contains whitespace

Remove whitespace from blank line

(W293)


136-136: print found

Remove print

(T201)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


139-139: print found

Remove print

(T201)


141-141: Trailing whitespace

Remove trailing whitespace

(W291)


141-141: No newline at end of file

Add trailing newline

(W292)

ComfyUI/tests/api_nodes/test_runway_text2img.py

15-15: Blank line contains whitespace

Remove whitespace from blank line

(W293)


21-21: Blank line contains whitespace

Remove whitespace from blank line

(W293)


25-25: Blank line contains whitespace

Remove whitespace from blank line

(W293)


28-28: Blank line contains whitespace

Remove whitespace from blank line

(W293)


32-32: Blank line contains whitespace

Remove whitespace from blank line

(W293)


42-42: Blank line contains whitespace

Remove whitespace from blank line

(W293)


45-45: Blank line contains whitespace

Remove whitespace from blank line

(W293)


56-56: Blank line contains whitespace

Remove whitespace from blank line

(W293)


60-60: Blank line contains whitespace

Remove whitespace from blank line

(W293)


73-73: Blank line contains whitespace

Remove whitespace from blank line

(W293)


80-80: Blank line contains whitespace

Remove whitespace from blank line

(W293)


86-86: Blank line contains whitespace

Remove whitespace from blank line

(W293)


89-89: Blank line contains whitespace

Remove whitespace from blank line

(W293)


128-128: f-string without any placeholders

Remove extraneous f prefix

(F541)


180-180: Blank line contains whitespace

Remove whitespace from blank line

(W293)

ComfyUI/comfy_api_nodes/nodes_runway.py

18-18: base64 imported but unused

Remove unused import: base64

(F401)


25-25: Redefinition of unused Optional from line 14

Remove definition: Optional

(F811)


27-27: nodes imported but unused

Remove unused import: nodes

(F401)


28-28: Redefinition of unused torch from line 22

Remove definition: torch

(F811)


29-29: Redefinition of unused time from line 20

Remove definition: time

(F811)


648-648: Blank line contains whitespace

Remove whitespace from blank line

(W293)


654-654: Blank line contains whitespace

Remove whitespace from blank line

(W293)


672-672: Blank line contains whitespace

Remove whitespace from blank line

(W293)


676-676: Blank line contains whitespace

Remove whitespace from blank line

(W293)


680-680: Blank line contains whitespace

Remove whitespace from blank line

(W293)


683-683: Blank line contains whitespace

Remove whitespace from blank line

(W293)


696-696: Blank line contains whitespace

Remove whitespace from blank line

(W293)


700-700: Blank line contains whitespace

Remove whitespace from blank line

(W293)


708-708: Blank line contains whitespace

Remove whitespace from blank line

(W293)


712-712: Trailing whitespace

Remove trailing whitespace

(W291)


714-714: Blank line contains whitespace

Remove whitespace from blank line

(W293)


721-721: Blank line contains whitespace

Remove whitespace from blank line

(W293)


764-764: Blank line contains whitespace

Remove whitespace from blank line

(W293)


774-774: Blank line contains whitespace

Remove whitespace from blank line

(W293)


776-776: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


777-777: print found

Remove print

(T201)


805-805: Trailing whitespace

Remove trailing whitespace

(W291)


805-805: No newline at end of file

Add trailing newline

(W292)

🔇 Additional comments (3)
ComfyUI/user/default/comfy.settings.json (1)

3-3: Double-check version consistency across the repo

Before merging, verify that 0.3.48 matches the version exposed by setup.py, __init__.__version__, Docker labels, etc., to avoid user confusion.

ComfyUI/user/default/workflows/runway_text2img_workflow.json (1)

6-6: Aspect ratio format is supported

The RunwayTextToImageAspectRatioEnum definition includes "1024:1024" as a valid member, so using "1024:1024" in the workflow is fully supported by RunwayText2ImgNode. No changes required.

ComfyUI/comfy_api_nodes/nodes_runway.py (1)

636-638: Good defensive programming practice!

Adding an explicit check for image_url before using it prevents potential runtime errors in edge cases where get_image_url_from_task_status might return None.

@darcy3000
Copy link
Contributor

Hey! Can you please post the PR in discord? We can discuss further there. Also don't forget to include the UI screenshot and generated image in the PR description.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
ComfyUI/comfy_api_nodes/nodes_runway.py (7)

679-679: Make timeout configurable or add better timeout handling.

The fixed 60-second timeout may not be sufficient in all environments. Consider making it configurable or adding better timeout exception handling.

-    def generate(self, prompt: str, ratio: str, unique_id: Optional[str] = None, timeout: int = 60, **kwargs):
+    def generate(self, prompt: str, ratio: str, unique_id: Optional[str] = None, timeout: int = 60, **kwargs):

Add timeout exception handling in the try-except block:

+        except requests.exceptions.Timeout:
+            raise RunwayApiError(f"Request to Runway API timed out after {timeout} seconds")

723-723: Consider exponential backoff for polling.

Fixed 1-second polling intervals may unnecessarily delay result retrieval. Consider implementing exponential backoff or shorter initial intervals.

-            interval = 1.0
+            # Exponential backoff parameters
+            initial_interval = 0.5  # seconds
+            max_interval = 4.0      # seconds
+            backoff_factor = 1.5
+            interval = initial_interval
+            
             for attempt in range(self.MAX_POLL_ATTEMPTS):
                 time.sleep(interval)
                 status_response = requests.get(status_url, headers=headers)
                 status_response.raise_for_status()
                 status_data = status_response.json()
+                interval = min(interval * backoff_factor, max_interval)

639-785: Significant code duplication - consider using existing RunwayTextToImageNode.

This new node duplicates the functionality of the existing RunwayTextToImageNode (lines 499-631). Both generate images from text prompts using Runway's API, support the same aspect ratios, handle polling, and convert images to tensors.

The existing node uses established abstractions (ApiEndpoint, SynchronousOperation, PollingOperation) which provide consistent error handling, authentication, and progress tracking.

Consider either:

  1. Using the existing RunwayTextToImageNode directly
  2. Extending it if additional functionality is needed
  3. Creating a wrapper that delegates to the existing implementation

This would maintain consistency with the codebase architecture and avoid maintenance burden.


672-677: Authentication approach inconsistent with other Runway nodes.

The node reads the API key directly from environment variables (line 684), while other Runway nodes use the auth_kwargs pattern with hidden inputs (auth_token, comfy_api_key).

Although the hidden inputs are defined here, they're not used in the implementation. Update the generate method to use auth_kwargs instead of direct environment variable access to maintain consistency.


693-694: Use established proxy endpoints instead of direct API URLs.

The node uses hardcoded API URLs instead of the proxy endpoints defined at the top of the file (PATH_TEXT_TO_IMAGE, PATH_GET_TASK_STATUS).

-        api_base_url = "https://api.dev.runwayml.com/v1"
-        url = f"{api_base_url}/text_to_image"
+        url = f"{kwargs.get('api_base_url', '')}{PATH_TEXT_TO_IMAGE}"
-            status_url = f"{api_base_url}/tasks/{result_id}"
+            status_url = f"{kwargs.get('api_base_url', '')}{PATH_GET_TASK_STATUS}/{result_id}"

Also applies to: 718-718


686-686: Replace generic exceptions with specific exception types.

Multiple locations use generic ValueError, RuntimeError, and TimeoutError instead of the established RunwayApiError for consistency.

-            raise ValueError("RUNWAY_API_KEY environment variable is missing.")
+            raise RunwayApiError("RUNWAY_API_KEY environment variable is missing.")
-            raise ValueError("Prompt cannot be empty.")
+            raise RunwayApiError("Prompt cannot be empty.")
-                raise RuntimeError("No result ID returned from Runway API.")
+                raise RunwayApiError("No result ID returned from Runway API.")
-                    raise RuntimeError(f"Runway task failed with status: {status_data['status']}")
+                    raise RunwayApiError(f"Runway task failed with status: {status_data['status']}")
-                raise TimeoutError("Image generation timed out.")
+                raise RunwayApiError("Image generation timed out.")

Also applies to: 690-690, 715-715, 734-734, 737-737


740-740: Add timeout for image download request.

The image download request lacks a timeout specification, which could cause indefinite hangs.

-            img_resp = requests.get(image_url)
+            img_resp = requests.get(image_url, timeout=timeout)
🧹 Nitpick comments (1)
ComfyUI/nodes.py (1)

1584-1598: Fix whitespace issue and approve tensor validation improvements

The tensor validation and processing improvements look excellent for supporting the new Runway node output format. The dimension handling, error messages, and pixel value scaling are all implemented correctly.

However, there's a whitespace issue on line 1595 flagged by static analysis:

 else:
    img_np = np.clip(img_np, 0, 255).astype(np.uint8)
-
+
 if img_np.shape[2] != 3:
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d66a93 and bd16f4e.

📒 Files selected for processing (5)
  • ComfyUI/comfy_api_nodes/nodes_runway.py (2 hunks)
  • ComfyUI/nodes.py (2 hunks)
  • ComfyUI/user/default/comfy.settings.json (1 hunks)
  • ComfyUI/user/default/workflows/runway_advanced_workflow.json (1 hunks)
  • ComfyUI/user/default/workflows/runway_text2img_workflow.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • ComfyUI/user/default/comfy.settings.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • ComfyUI/user/default/workflows/runway_advanced_workflow.json
  • ComfyUI/user/default/workflows/runway_text2img_workflow.json
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: in the lumaphotondepth2img node (comfyui/custom_nodes/luma_photon_node/luma_photon_node.py), the wei...
Learnt from: divyaprakash0426
PR: DreamLayer-AI/DreamLayer#34
File: ComfyUI/custom_nodes/luma_photon_node/luma_photon_node.py:224-224
Timestamp: 2025-07-16T05:27:07.946Z
Learning: In the LumaPhotonDepth2Img node (ComfyUI/custom_nodes/luma_photon_node/luma_photon_node.py), the weight transformation `1.0 - image_weight` is intentional and functions as designed, despite the tooltip indicating higher values should preserve the image more.

Applied to files:

  • ComfyUI/nodes.py
🪛 Ruff (0.12.2)
ComfyUI/nodes.py

1595-1595: Blank line contains whitespace

Remove whitespace from blank line

(W293)

ComfyUI/comfy_api_nodes/nodes_runway.py

691-691: Blank line contains whitespace

Remove whitespace from blank line

(W293)


716-716: Blank line contains whitespace

Remove whitespace from blank line

(W293)


720-720: Blank line contains whitespace

Remove whitespace from blank line

(W293)


758-758: Trailing whitespace

Remove trailing whitespace

(W291)


774-774: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


801-801: No newline at end of file

Add trailing newline

(W292)

🔇 Additional comments (4)
ComfyUI/nodes.py (1)

1802-1811: LGTM! Simplified tensor handling is more efficient

The removal of unnecessary tensor dimension permutations is a good optimization. The assumption that input tensors are already in [B, C, H, W] format aligns well with the Runway node output and eliminates redundant operations.

The aspect ratio calculation logic is preserved correctly, and the direct call to comfy.utils.common_upscale is appropriate.

ComfyUI/comfy_api_nodes/nodes_runway.py (3)

16-26: LGTM - necessary imports for new functionality.

The new imports are all used in the RunwayText2ImgNode implementation and are appropriate for the direct API integration approach.


629-630: Good defensive programming improvement.

Adding explicit validation for the image URL extraction prevents potential runtime errors and provides clearer error messaging.


792-792: Node mappings correctly implemented.

The new node is properly registered in both mapping dictionaries with an appropriate display name that distinguishes it from the existing node.

Also applies to: 800-800

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
ComfyUI/tests/api_nodes/test_runway_integration.py (1)

1-138: Comprehensive integration test suite with existing feedback.

The test structure is thorough, validating file existence, content, AST parsing, mappings, and import capabilities. However, previous review comments have already identified several code quality improvements needed.

ComfyUI/comfy_api_nodes/nodes_runway.py (1)

639-813: Acknowledge comprehensive architectural feedback.

Previous review comments have identified significant architectural issues with this implementation, including code duplication with RunwayTextToImageNode, inconsistent authentication patterns, and bypassing established proxy endpoints. These are valid concerns that should be addressed to maintain codebase consistency and reduce maintenance overhead.

🧹 Nitpick comments (1)
ComfyUI/tests/api_nodes/test_runway_text2img.py (1)

10-10: Remove unused import.

The torch import is not used in this test file.

-import torch
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd16f4e and febbd05.

📒 Files selected for processing (5)
  • ComfyUI/comfy_api_nodes/nodes_runway.py (2 hunks)
  • ComfyUI/tests/api_nodes/test_runway_integration.py (1 hunks)
  • ComfyUI/tests/api_nodes/test_runway_text2image.py (1 hunks)
  • ComfyUI/tests/api_nodes/test_runway_text2img.py (1 hunks)
  • ComfyUI/user/default/workflows/runway_advanced_workflow.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ComfyUI/user/default/workflows/runway_advanced_workflow.json
🧰 Additional context used
🧬 Code Graph Analysis (1)
ComfyUI/tests/api_nodes/test_runway_text2image.py (1)
ComfyUI/comfy_api_nodes/nodes_runway.py (2)
  • RunwayText2ImgNode (639-796)
  • generate (679-796)
🪛 Ruff (0.12.2)
ComfyUI/tests/api_nodes/test_runway_text2image.py

4-4: base64 imported but unused

Remove unused import: base64

(F401)


6-6: numpy imported but unused

Remove unused import: numpy

(F401)


10-10: ComfyUI.utils.json_util.merge_json_recursive imported but unused

Remove unused import: ComfyUI.utils.json_util.merge_json_recursive

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


34-34: Blank line contains whitespace

Remove whitespace from blank line

(W293)


54-54: Blank line contains whitespace

Remove whitespace from blank line

(W293)


60-60: Blank line contains whitespace

Remove whitespace from blank line

(W293)


63-63: Blank line contains whitespace

Remove whitespace from blank line

(W293)


72-72: Undefined name requests

(F821)


76-76: Blank line contains whitespace

Remove whitespace from blank line

(W293)

ComfyUI/comfy_api_nodes/nodes_runway.py

691-691: Blank line contains whitespace

Remove whitespace from blank line

(W293)


716-716: Blank line contains whitespace

Remove whitespace from blank line

(W293)


720-720: Blank line contains whitespace

Remove whitespace from blank line

(W293)


758-758: Trailing whitespace

Remove trailing whitespace

(W291)


774-774: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


785-785: Blank line contains whitespace

Remove whitespace from blank line

(W293)


813-813: No newline at end of file

Add trailing newline

(W292)

ComfyUI/tests/api_nodes/test_runway_integration.py

9-9: torch imported but unused

Remove unused import: torch

(F401)


18-18: Blank line contains whitespace

Remove whitespace from blank line

(W293)


29-29: Blank line contains whitespace

Remove whitespace from blank line

(W293)


32-32: Blank line contains whitespace

Remove whitespace from blank line

(W293)


35-35: print found

Remove print

(T201)


36-36: Blank line contains whitespace

Remove whitespace from blank line

(W293)


43-43: Blank line contains whitespace

Remove whitespace from blank line

(W293)


46-46: print found

Remove print

(T201)


47-47: Blank line contains whitespace

Remove whitespace from blank line

(W293)


48-48: print found

Remove print

(T201)


55-55: Blank line contains whitespace

Remove whitespace from blank line

(W293)


59-59: Blank line contains whitespace

Remove whitespace from blank line

(W293)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)


68-68: print found

Remove print

(T201)


68-68: f-string without any placeholders

Remove extraneous f prefix

(F541)


69-69: Blank line contains whitespace

Remove whitespace from blank line

(W293)


76-76: print found

Remove print

(T201)


81-81: Blank line contains whitespace

Remove whitespace from blank line

(W293)


83-83: Blank line contains whitespace

Remove whitespace from blank line

(W293)


94-94: Blank line contains whitespace

Remove whitespace from blank line

(W293)


97-97: Blank line contains whitespace

Remove whitespace from blank line

(W293)


101-101: Blank line contains whitespace

Remove whitespace from blank line

(W293)


104-104: Blank line contains whitespace

Remove whitespace from blank line

(W293)


105-105: print found

Remove print

(T201)


112-112: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Blank line contains whitespace

Remove whitespace from blank line

(W293)


129-129: Blank line contains whitespace

Remove whitespace from blank line

(W293)


132-132: print found

Remove print

(T201)


133-133: Blank line contains whitespace

Remove whitespace from blank line

(W293)


135-135: print found

Remove print

(T201)


137-137: Trailing whitespace

Remove trailing whitespace

(W291)

ComfyUI/tests/api_nodes/test_runway_text2img.py

10-10: torch imported but unused

Remove unused import: torch

(F401)


45-45: Blank line contains whitespace

Remove whitespace from blank line

(W293)


54-54: Blank line contains whitespace

Remove whitespace from blank line

(W293)

🔇 Additional comments (5)
ComfyUI/tests/api_nodes/test_runway_text2image.py (4)

12-45: Well-structured integration test.

The test properly mocks the complete API flow including task creation, polling, and image download. Good validation of the tensor output format with correct assertions on shape and type.


46-56: Good error handling test.

Properly validates that the node raises a ValueError when the required RUNWAY_API_KEY environment variable is missing.


57-65: Good input validation test.

Properly tests that the node validates prompt input and raises appropriate errors for empty or whitespace-only prompts.


66-78: HTTP error handling test looks good.

The test structure properly validates 401 Unauthorized responses, but this depends on the missing requests import being fixed.

ComfyUI/tests/api_nodes/test_runway_text2img.py (1)

14-102: Well-organized test structure.

Good use of helper functions to abstract path management and file operations. The test functions are focused and clear, properly validating the node's presence, structure, and functionality.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
ComfyUI/comfy_api_nodes/nodes_runway.py (4)

639-784: Critical: This implementation duplicates existing functionality and has multiple architectural issues

This new node significantly duplicates the existing RunwayTextToImageNode functionality, which violates DRY principles and creates maintenance overhead.

Key issues that need to be addressed:

  1. Code duplication: The existing RunwayTextToImageNode already provides this functionality with better architecture
  2. Authentication inconsistency: Direct environment variable access instead of using the established auth_kwargs pattern
  3. Proxy bypass: Hardcoded API URLs instead of using the configured proxy endpoints
  4. Error handling: Multiple exception handling blocks and inconsistent error types

Recommended approach: Either extend the existing RunwayTextToImageNode or create a wrapper that delegates to it, maintaining consistency with the established codebase patterns.


684-686: Use consistent authentication pattern

The node reads the API key directly from environment variables, which is inconsistent with other Runway nodes that use the auth_kwargs pattern.

Follow the established pattern by using the auth_kwargs parameter that's already available in the method signature.


693-694: Use proxy endpoints instead of hardcoded URLs

The hardcoded API URLs bypass the proxy configuration defined at the top of the file.

Replace with the established proxy endpoints:

-        url = f"{api_base_url}/text_to_image"
+        url = f"{kwargs.get('api_base_url', '')}{PATH_TEXT_TO_IMAGE}"
-            status_url = f"{api_base_url}/tasks/{result_id}"
+            status_url = f"{kwargs.get('api_base_url', '')}{PATH_GET_TASK_STATUS}/{result_id}"

Also applies to: 718-718


774-784: Fix error handling inconsistencies

The error handling has several issues that were flagged in previous reviews but remain unaddressed.

Apply this fix:

-        except requests.exceptions.HTTPError:
+        except requests.exceptions.HTTPError:
             if response.status_code == 401:
-                raise RunwayApiError("Invalid Runway API key. Please check your RUNWAY_API_KEY.")
+                raise RunwayApiError("Invalid Runway API key. Please check your RUNWAY_API_KEY.")
             elif response.status_code == 400:
-                raise RunwayApiError(f"Bad request: {response.text}")
+                raise RunwayApiError(f"Bad request: {response.text}")
             else:
-                raise RunwayApiError(f"Runway API error (HTTP {response.status_code}): {response.text}")
+                raise RunwayApiError(f"Runway API error (HTTP {response.status_code}): {response.text}")
         except requests.exceptions.RequestException as e:
-            raise RunwayApiError(f"Failed to connect to Runway API: {str(e)}")
+            raise RunwayApiError(f"Failed to connect to Runway API: {str(e)}")
         except Exception as e:
-            raise RunwayApiError(f"Unhandled error: {str(e)}")
+            raise RunwayApiError(f"Unhandled error: {str(e)}")
🧹 Nitpick comments (1)
ComfyUI/comfy_api_nodes/nodes_runway.py (1)

792-792: Node mappings correctly added

The new node is properly registered in both class and display name mappings.

Fix the missing newline at end of file:

     "RunwayText2ImgNode": "Runway Text to Image (Simple)",
-}
+}
+

Also applies to: 800-801

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between febbd05 and 409c61d.

📒 Files selected for processing (1)
  • ComfyUI/comfy_api_nodes/nodes_runway.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
ComfyUI/comfy_api_nodes/nodes_runway.py

691-691: Blank line contains whitespace

Remove whitespace from blank line

(W293)


716-716: Blank line contains whitespace

Remove whitespace from blank line

(W293)


720-720: Blank line contains whitespace

Remove whitespace from blank line

(W293)


758-758: Trailing whitespace

Remove trailing whitespace

(W291)


773-773: Blank line contains whitespace

Remove whitespace from blank line

(W293)


801-801: No newline at end of file

Add trailing newline

(W292)

🔇 Additional comments (2)
ComfyUI/comfy_api_nodes/nodes_runway.py (2)

16-26: LGTM - Necessary imports for new functionality

The added imports are required for the direct API implementation in the new RunwayText2ImgNode class.


629-630: Good defensive programming practice

Adding validation for the image URL presence improves error handling for edge cases where the API response format might be unexpected.

Copy link
Contributor Author

@ElenkaSan ElenkaSan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All changes provided, please PR ready for review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants