diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8560b79 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Declare files that always have LF line endings on checkout +* text eol=lf diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..00769f2 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Code of Conduct + +Be openness, as well as friendly and didactic in discussions. + +Treat everybody equally, and value their contributions. + +Decisions are made based on technical merit and consensus. + +Try to follow most principles described here: https://nextcloud.com/code-of-conduct/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee33183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,150 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +converted/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Pycharm settings +.idea/ + +# VSCode workspace settings +.vscode + +.DS_Store + +/out +/dev/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e84f372 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,32 @@ +exclude: ^(screenshots)/ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-yaml + - id: check-toml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: mixed-line-ending + +- repo: https://github.com/PyCQA/isort + rev: 5.13.2 + hooks: + - id: isort + files: . + +- repo: https://github.com/psf/black + rev: 24.10.0 + hooks: + - id: black + files: . + +- repo: https://github.com/tox-dev/pyproject-fmt + rev: 2.3.1 + hooks: + - id: pyproject-fmt + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.9 + hooks: + - id: ruff diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6c489ca --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + + +## [1.0.0 - 2025-03-12] + +### Added + +- First release. diff --git a/LICENSE b/LICENSE index 261eeb9..d028b1e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +MIT License - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Copyright (c) 2025 Visionatrix - 1. Definitions. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6f63c5e..4c80a9d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -# ComfyUI-RemoteVAE -VAE decoding on HuggingFace +# Simple ComfyUI Node for Remote VAE Decoding + +This node enables the use of VAE decoding through HuggingFace's inference endpoints. + +For more details, refer to the [VAE Decode with Hybrid Inference](https://huggingface.co/docs/diffusers/main/en/hybrid_inference/vae_decode) documentation. + +> **Note:** This node does not support **SD1.5** or **SDXL** VAEs, as they typically perform faster on local hardware. + +Currently, only **Flux** and **HunyuanVideo** are supported. + +As HuggingFace expands [support for other VAEs](https://huggingface.co/docs/diffusers/main/en/hybrid_inference/vae_decode#available-vaes), they will be incorporated into this node. + +This node does not rely on the `diffusers` library and has no external dependencies beyond ComfyUI-Core. It is designed to be minimalistic. + +For more information on remote VAE decoding, you can refer to HuggingFace's [blog post](https://huggingface.co/blog/remote_vae) + +![Usage](https://raw.githubusercontent.com/Visionatrix/ComfyUI-RemoteVAE/main/screenshots/usage.jpg) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..2e96bd6 --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS + +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] \ No newline at end of file diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..e661fcb --- /dev/null +++ b/nodes.py @@ -0,0 +1,99 @@ +import torch +import json +import requests + +from safetensors.torch import _tobytes + +DTYPE_MAP = { + "float16": torch.float16, + "float32": torch.float32, + "bfloat16": torch.bfloat16, + "uint8": torch.uint8, +} + +def remote_decode( + endpoint: str, + tensor: torch.Tensor, + height: int, + width: int, +) -> torch.Tensor: + if not height or not width: + raise ValueError("`height` and `width` required for packed latents.") + parameters = { + "do_scaling": False, + "output_type": "pt", + "partial_postprocess": False, + "shape": list(tensor.shape), + "dtype": str(tensor.dtype).split(".")[-1], + "height": height, + "width": width, + } + response = requests.post(endpoint, params=parameters, data=_tobytes(tensor, "tensor"), headers={ + "Content-Type": "tensor/binary", + "Accept": "tensor/binary", + }) + if not response.ok: + raise RuntimeError(response.json()) + parameters = response.headers + return torch.frombuffer(bytearray(response.content), dtype=DTYPE_MAP[parameters["dtype"]]).reshape(json.loads(parameters["shape"])) + + +class RemoteVAEDecode: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "samples": ("LATENT",), + "vae_type": (["Flux", "HunyuanVideo"],), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "decode" + CATEGORY = "RemoteVae" + + def decode(self, samples, vae_type): + latents = samples["samples"] + vae_scale_factor = 8 + + if vae_type == "HunyuanVideo": + endpoint = "https://o7ywnmrahorts457.us-east-1.aws.endpoints.huggingface.cloud/" + elif vae_type == "Flux": + endpoint = "https://whhx50ex1aryqvw6.us-east-1.aws.endpoints.huggingface.cloud/" + else: + raise ValueError("Unsupported VAE_type provided.") + + result = remote_decode( + endpoint=endpoint, + tensor=latents, + height=latents.shape[2] * vae_scale_factor, + width=latents.shape[3] * vae_scale_factor, + ) + + if vae_type == "HunyuanVideo": + out = self.postprocess_video(result)[0].permute(0, 2, 3, 1).cpu().float() + else: + out = self.denormalize(result).permute(0, 2, 3, 1).cpu().float() + return (out,) + + @staticmethod + def denormalize(images: torch.Tensor) -> torch.Tensor: + """Denormalize an image tensor from [-1,1] to [0,1] as done in diffusers.""" + return (images * 0.5 + 0.5).clamp(0, 1) + + + def postprocess_video(self, video: torch.Tensor) -> torch.Tensor: + """Simplified postprocess_video function.""" + outputs = [] + for i in range(video.shape[0]): + outputs.append(self.denormalize(video[i].permute(1, 0, 2, 3))) + return torch.stack(outputs) # [batch, frames, channels, height, width] + + +NODE_CLASS_MAPPINGS = { + "RemoteVAEDecode": RemoteVAEDecode, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "RemoteVAEDecode": "RemoteVAEDecode", +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6a6f6a7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,57 @@ +[project] +name = "comfyui-remotevae" +version = "1.0.0" +description = "ComfyUI Node for Remote VAE Decoding." +license = { file = "LICENSE" } +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [] +urls.Changelog = "https://github.com/Visionatrix/ComfyUI-RemoteVAE/blob/main/CHANGELOG.md" +urls.Repository = "https://github.com/Visionatrix/ComfyUI-RemoteVAE" +urls.Source = "https://github.com/Visionatrix/ComfyUI-RemoteVAE" + +[tool.black] +line-length = 120 +preview = true + +[tool.ruff] +target-version = "py310" +line-length = 120 +lint.select = [ + "A", + "B", + "C", + "E", + "F", + "G", + "I", + "PIE", + "Q", + "RET", + "RUF", + "S", + "SIM", + "UP", + "W", +] +lint.extend-ignore = [ + "I001", + "RUF100", + "S311", + "S603", +] +lint.mccabe.max-complexity = 20 + +[tool.isort] +profile = "black" + +[tool.comfy] +PublisherId = "visionatrix" +DisplayName = "ComfyUI-RemoteVAE" +Icon = "https://raw.githubusercontent.com/Visionatrix/VixFlowsDocs/main/screenshots/logo_org_800x800.png" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1c15243 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +torch +pillow +numpy +safetensors +requests diff --git a/screenshots/usage.png b/screenshots/usage.png new file mode 100644 index 0000000..07ece88 Binary files /dev/null and b/screenshots/usage.png differ