File size: 6,288 Bytes
c24f7c5
a29d7ef
 
12eaa16
c24f7c5
12eaa16
 
c24f7c5
 
12eaa16
c24f7c5
a29d7ef
aa3776e
526f8c3
 
12eaa16
 
aa3776e
 
526f8c3
 
aa3776e
526f8c3
 
12eaa16
526f8c3
12eaa16
 
526f8c3
6a078c5
6275165
6a078c5
a29d7ef
 
aa3776e
12eaa16
a49802e
6a078c5
 
 
 
dffb4a8
95c986b
6a078c5
 
 
 
6275165
 
dffb4a8
6a078c5
a29d7ef
 
6a078c5
a29d7ef
46fdc1c
 
c24f7c5
a29d7ef
aa3776e
c62ade5
a29d7ef
c24f7c5
 
 
dffb4a8
 
 
 
6275165
 
 
c24f7c5
6a078c5
 
 
 
 
 
 
 
 
 
 
aa3776e
 
 
 
 
 
 
 
6a078c5
aa3776e
dffb4a8
aa3776e
 
 
 
 
 
 
 
 
 
 
 
 
 
6a078c5
aa3776e
12eaa16
 
 
ff46616
12eaa16
 
 
aa3776e
12eaa16
aa3776e
 
ff46616
 
6a078c5
aa3776e
6a078c5
12eaa16
6a078c5
 
 
 
 
 
 
 
 
 
 
aa3776e
 
 
 
 
a29d7ef
 
6a078c5
a29d7ef
c24f7c5
 
aa3776e
c24f7c5
 
 
dffb4a8
 
 
 
 
c24f7c5
 
 
 
a29d7ef
 
 
 
 
c24f7c5
a29d7ef
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import os
import sys
import subprocess
import time
import json
import urllib.request
import urllib.parse
import gradio as gr
import torch
import spaces

# ==========================================
# 1. Install Dependencies Natively at Startup
# ==========================================
required_packages = [
    "safetensors", "scipy", "tqdm", "psutil", "einops", 
    "transformers", "tokenizers", "sentencepiece", "torchsde", 
    "huggingface-hub", "aiohttp", "yarl", "av", "blake3",
    "sqlalchemy", "alembic", "comfy-aimdo"
]

print("Checking system requirements...")
for package in required_packages:
    try:
        __import__(package.replace("-", "_"))
    except ImportError:
        print(f"Installing missing dependency: {package}")
        subprocess.run([sys.executable, "-m", "pip", "install", package], check=True)

# ==========================================
# 2. Clone ComfyUI & Download Custom Model
# ==========================================
COMFYUI_DIR = os.path.abspath("ComfyUI")
if not os.path.exists(COMFYUI_DIR):
    print("Cloning ComfyUI framework...")
    subprocess.run(["git", "clone", "https://github.com/comfyanonymous/ComfyUI.git", COMFYUI_DIR], check=True)

# Ensure the models directory exists
CHECKPOINT_DIR = os.path.join(COMFYUI_DIR, "models", "checkpoints")
os.makedirs(CHECKPOINT_DIR, exist_ok=True)

# Download the exact model your workflow requires
model_filename = "epicphotogasm_ultimateFidelity.safetensors"
model_path = os.path.join(CHECKPOINT_DIR, model_filename)

if not os.path.exists(model_path):
    print(f"Downloading {model_filename} (This may take a few minutes)...")
    # Public HuggingFace mirror for the EpicPhotogasm checkpoint
    model_url = "https://huggingface.co/sibylexpe/ModelsSD15/resolve/main/epicphotogasm_ultimateFidelity.safetensors"
    subprocess.run(["wget", "-q", "-O", model_path, model_url], check=True)


# ==========================================
# 3. Dynamic GPU Inference Function
# ==========================================
# FIX: Capitalized GPU here
@spaces.GPU(duration=120)
def generate_image(user_prompt):
    if not os.path.exists("workflow_api.json"):
        print("Error: workflow_api.json missing from root directory.")
        return None  
        
    with open("workflow_api.json", "r") as f:
        prompt_workflow = json.load(f)

    # Automatically patch the SD3 vs SD 1.5 Latent Mismatch
    if "68" in prompt_workflow and prompt_workflow["68"].get("class_type") == "EmptySD3LatentImage":
        prompt_workflow["68"]["class_type"] = "EmptyLatentImage"

    # Inject your prompt into the correct CLIP Text Encode node ID (67)
    if "67" in prompt_workflow and "inputs" in prompt_workflow["67"]:
        prompt_workflow["67"]["inputs"]["text"] = user_prompt

    # Clear old remnants in both output and temp directories
    search_dirs = [os.path.join(COMFYUI_DIR, "output"), os.path.join(COMFYUI_DIR, "temp")]
    for d in search_dirs:
        if os.path.exists(d):
            for file in os.listdir(d):
                try:
                    os.remove(os.path.join(d, file))
                except Exception:
                    pass

    # Launch ComfyUI inside the GPU environment
    print("ZeroGPU allocated. Launching ComfyUI server instance...")
    comfy_process = subprocess.Popen(
        [sys.executable, os.path.join(COMFYUI_DIR, "main.py"), "--listen", "127.0.0.1", "--port", "8188", "--highvram"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True
    )

    # Poll local port until the server is alive
    server_ready = False
    for _ in range(25):
        time.sleep(1)
        try:
            with urllib.request.urlopen("http://127.0.0.1:8188/history", timeout=1) as r:
                if r.status == 200:
                    server_ready = True
                    break
        except Exception:
            continue

    if not server_ready:
        print("ComfyUI server failed to initialize within time constraints.")
        comfy_process.terminate()
        return None

    # Enqueue the workflow
    print("Server online. Enqueueing workflow...")
    p = {"prompt": prompt_workflow}
    data = json.dumps(p).encode('utf-8')
    req = urllib.request.Request("http://127.0.0.1:8188/prompt", data=data, headers={'Content-Type': 'application/json'})
    
    try:
        with urllib.request.urlopen(req) as response:
            res = json.loads(response.read().decode('utf-8'))
            print(f"Workflow running. Prompt ID: {res['prompt_id']}")
    except Exception as e:
        print(f"API execution dispatch failed: {e}")
        comfy_process.terminate()
        return None

    # Track output directory for the compiled image asset
    generated_image_path = None
    for _ in range(90):  
        time.sleep(1)
        for d in search_dirs:
            if os.path.exists(d):
                files = [os.path.join(d, f) for f in os.listdir(d) if os.path.isfile(os.path.join(d, f))]
                if files:
                    generated_image_path = max(files, key=os.path.getmtime)
                    break
        if generated_image_path:
            print(f"Asset generation complete: {generated_image_path}")
            break

    # Clean up the server process
    comfy_process.terminate()
    comfy_process.wait()
    
    return generated_image_path


# ==========================================
# 4. Gradio Web Interface Layout
# ==========================================
with gr.Blocks() as demo:
    gr.Markdown("# My Custom ComfyUI App")
    gr.Markdown("Enter a prompt below to run your custom ComfyUI workflow live via ZeroGPU allocations.")
    
    with gr.Row():
        with gr.Column():
            prompt_input = gr.Textbox(
                label="Prompt", 
                value="A 3D blocky rendering of a green creature in a tan robe and chest plate, dancing in a dedicated boombox setup. Bright colors, bold lines, blocky cel shading.",
                lines=5
            )
            submit_btn = gr.Button("Generate")
        with gr.Column():
            image_output = gr.Image(label="Result")
            
    submit_btn.click(
        fn=generate_image,
        inputs=prompt_input,
        outputs=image_output
    )

if __name__ == "__main__":
    demo.launch()