import json import os import subprocess import uuid from pathlib import Path from typing import Dict import modal image = ( # build up a Modal Image to run ComfyUI, step by step modal.Image.debian_slim( # start from basic Linux with Python python_version="3.10" ) .apt_install("git") # install git to clone ComfyUI .pip_install("fastapi[standard]==0.115.4") # install web dependencies .pip_install("comfy-cli==1.3.5") # install comfy-cli .pip_install("cos-python-sdk-v5") .pip_install("sqlalchemy") .pip_install("ultralytics") .pip_install("tencentcloud-sdk-python") .pip_install("pymysql") .pip_install("Pillow") .pip_install("ffmpy") .pip_install("opencv-python") .run_commands( # use comfy-cli to install ComfyUI and its dependencies "comfy --skip-prompt install --nvidia --version 0.3.10" ).add_local_file("snapshot.json","/root/snapshot.json", copy=True) ) image = ( image.run_commands("comfy node install https://github.com/M1kep/ComfyLiterals") .run_commands("comfy node install https://github.com/evanspearman/ComfyMath") .run_commands("comfy node install https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-Bowong.git") .run_commands("comfy node install https://github.com/crystian/ComfyUI-Crystools") .run_commands("comfy node install https://github.com/pythongosssss/ComfyUI-Custom-Scripts") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-CustomNode.git") .run_commands("comfy node install https://github.com/BennyKok/comfyui-deploy") .run_commands("comfy node install https://github.com/yolain/ComfyUI-Easy-Use") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-LatentSync-Node.git") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-VideoHelperSuite.git") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/CosyVoice-ComfyUI.git") .run_commands("comfy node install https://github.com/jags111/efficiency-nodes-comfyui") .run_commands("comfy node install https://github.com/WASasquatch/was-node-suite-comfyui") .run_commands("comfy node install https://github.com/rgthree/rgthree-comfy") .run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials") .run_commands("comfy node install https://github.com/melMass/comfy_mtb") .run_commands( "mkdir -p /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model && rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model && ln -s /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model" ).run_commands( "mkdir -p /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints && rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints && ln -s /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints" ).run_commands( "mkdir -p /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && mkdir -p /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && rm -rf /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && ln -s /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT" ).run_commands( "mkdir -p /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints && mkdir -p /root/.cache/torch/hub/checkpoints && rm -rf /root/.cache/torch/hub/checkpoints && ln -s /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints /root/.cache/torch/hub/checkpoints" ).run_commands( "mkdir -p /root/comfy/ComfyUI/models/stabilityai && ln -s /root/comfy/ComfyUI/models/stabilityai /root/comfy/ComfyUI/stabilityai" ).run_commands( "rm -rf /root/comfy/ComfyUI/models" ).run_commands( "apt update && apt install -y ffmpeg && ffmpeg -version" ).pip_install("av") # Add .run_commands(...) calls for any other custom nodes you want to download ) image = image.add_local_file( "highlight_v0.113_api.json", "/root/workflow_api.json" ) app = modal.App(name="highlight-comfyui", image=image) vol = modal.Volume.from_name("comfyui-model", create_if_missing=True) secret = modal.Secret.from_name("aws-s3-secret") @app.cls( allow_concurrent_inputs=1, # allow 10 concurrent API calls concurrency_limit=50, container_idle_timeout=120, # 5 minute container keep alive after it processes an input; increasing this value is a great way to reduce ComfyUI cold start times timeout=800, gpu="T4", secrets=[secret], volumes={ "/root/comfy/ComfyUI/models": vol, # "/root/comfy/ComfyUI/input": modal.CloudBucketMount( # bucket_name=os.environ["BUCKET_INPUT"], # # bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"], # secret=secret, # key_prefix="comfyui" # ), # "/root/comfy/ComfyUI/output": modal.CloudBucketMount( # bucket_name=os.environ["BUCKET_OUTPUT"], # # bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"], # secret=secret, # key_prefix="comfyui" # ), }, ) class ComfyUI: @modal.enter() def launch_comfy_background(self): # starts the ComfyUI server in the background exactly once when the first input is received cmd = "comfy launch --background" subprocess.run(cmd, shell=True, check=True) @modal.method() def infer(self, workflow_json: str = ""): # runs the comfy run --workflow command as a subprocess session_id = str(uuid.uuid4()) with open(f"/root/{session_id}.json", "w", encoding="utf-8") as f: f.write(workflow_json) cmd = f"comfy run --workflow /root/{session_id}.json --wait --timeout 1200" # TODO 接收返回值 subprocess.run(cmd, shell=True, check=True) # completed workflows write output images to this directory output_dir = "/root/comfy/ComfyUI/output" # looks up the name of the output image file based on the workflow workflow = json.loads(workflow_json) file_prefix = [ node.get("inputs") for node in workflow.values() if node.get("class_type") == "VHS_VideoCombine" ][0]["filename_prefix"] # returns the image as bytes file_list = os.listdir(output_dir) # 获取按照文件时间创建排序的列表,默认是按时间升序 new_file_list = sorted(file_list, key=lambda file: os.path.getctime(os.path.join(output_dir, file)), reverse=True) print("file_list", new_file_list) for f in new_file_list: if f.startswith(file_prefix): return f @modal.web_endpoint(method="POST") def api(self, item: Dict): from fastapi import Response # save this updated workflow to a new file new_workflow_file = item["prompt"] # run inference on the currently running container fname = self.infer.local(new_workflow_file) j = { "file_name": fname } print("json", j) return Response(content=json.dumps(j), media_type="application/json")