File size: 11,235 Bytes
deb6554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf017c8
deb6554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/usr/bin/env python3
"""
RunPod Fresh Setup Script for Qwen Image Edit
==============================================
Downloads everything from YOUR OWN HuggingFace repos:
  - App code: sdfafdfsdf/Qwen-Image-Edit-App
  - Models:   sdfafdfsdf/Phr00t-Qwen-Rapid-AIO (20GB)
              sdfafdfsdf/Qwen-Image-Edit-2511   (16GB)
              sdfafdfsdf/BFS-Best-Face-Swap      (1.3GB)
              sdfafdfsdf/Qwen-Image-Edit-2511-Multiple-Angles-LoRA (282MB)
              sdfafdfsdf/vllm-flash-attn3        (807MB)

Usage:
  1. Upload this script to your RunPod
  2. Run: python3 setup_runpod.py
  3. The app will auto-start at the end

Set your HF token as an environment variable before running:
  export HF_TOKEN="hf_your_token_here"

Or pass it as a command line argument:
  python3 setup_runpod.py --token hf_your_token_here
"""

import os
import sys
import subprocess
import argparse
import time

# ═══════════════════════════════════════════════════════
# CONFIGURATION — Change these if you rename your repos
# ═══════════════════════════════════════════════════════

HF_USERNAME = "sdfafdfsdf"

# App code repo (small files: app.py, qwenimage/, camera_control_ui, etc.)
APP_REPO = f"{HF_USERNAME}/Qwen-Image-Edit-App"

# Model repos (large files: transformer weights, VAE, text encoder, etc.)
MODEL_REPOS = [
    f"{HF_USERNAME}/Phr00t-Qwen-Rapid-AIO",           # 20GB - AIO transformer
    f"{HF_USERNAME}/Qwen-Image-Edit-2511",              # 16GB - Pipeline (VAE, text encoder)
    f"{HF_USERNAME}/BFS-Best-Face-Swap",                 # 1.3GB - Face swap LoRA
    f"{HF_USERNAME}/Qwen-Image-Edit-2511-Multiple-Angles-LoRA",  # 282MB - Angles LoRA
    f"{HF_USERNAME}/vllm-flash-attn3",                   # 807MB - Flash attention kernel
    f"{HF_USERNAME}/QIE_2511_Consistency_Lora",          # Consistency LoRA
]

# Directories
WORKSPACE_DIR = "/workspace"
APP_DIR = os.path.join(WORKSPACE_DIR, "Qwen-Image-Edit")
VENV_DIR = os.path.join(WORKSPACE_DIR, "venv")
CACHE_DIR = os.path.join(WORKSPACE_DIR, "cache")
HF_HOME = os.path.join(CACHE_DIR, "huggingface")
PIP_CACHE = os.path.join(CACHE_DIR, "pip")
TMP_DIR = os.path.join(WORKSPACE_DIR, "tmp")


# ═══════════════════════════════════════════════════════
# HELPERS
# ═══════════════════════════════════════════════════════

def log(msg, level="INFO"):
    """Print a formatted log message."""
    icons = {"INFO": "ℹ️ ", "OK": "✅", "WARN": "⚠️ ", "ERR": "❌", "DL": "📦", "RUN": "🚀"}
    icon = icons.get(level, "  ")
    print(f"{icon} {msg}")


def run(command, cwd=None, env_extra=None, check=True):
    """Run a shell command, stream output, return exit code."""
    log(f"Running: {command}", "RUN")
    env = os.environ.copy()
    env["TMPDIR"] = TMP_DIR
    env["PIP_CACHE_DIR"] = PIP_CACHE
    env["HF_HOME"] = HF_HOME
    if env_extra:
        env.update(env_extra)

    proc = subprocess.Popen(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        text=True, cwd=cwd, env=env
    )
    for line in proc.stdout:
        print(line, end="")
    proc.wait()

    if proc.returncode != 0 and check:
        log(f"Command failed (exit {proc.returncode}): {command}", "ERR")
    return proc.returncode


def ensure_dirs():
    """Create all necessary directories."""
    for d in [APP_DIR, VENV_DIR, CACHE_DIR, HF_HOME, PIP_CACHE, TMP_DIR]:
        os.makedirs(d, exist_ok=True)


# ═══════════════════════════════════════════════════════
# STEP 1: Download app code
# ═══════════════════════════════════════════════════════

def download_app_code(token):
    """Download app code from your HF repo."""
    log(f"Downloading app code from {APP_REPO}...", "DL")

    pip = os.path.join(VENV_DIR, "bin", "pip")
    python = os.path.join(VENV_DIR, "bin", "python")

    # Use huggingface_hub to download
    run(f"""{python} -c "
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id='{APP_REPO}',
    repo_type='model',
    local_dir='{APP_DIR}',
    token='{token}'
)
print('App code downloaded successfully!')
" """)

    if os.path.exists(os.path.join(APP_DIR, "app.py")):
        log("App code downloaded successfully!", "OK")
    else:
        log("App code download may have failed — app.py not found!", "ERR")
        sys.exit(1)


# ═══════════════════════════════════════════════════════
# STEP 2: Download heavy models
# ═══════════════════════════════════════════════════════

def download_models(token):
    """Download all model repos (cached in HF_HOME)."""
    python = os.path.join(VENV_DIR, "bin", "python")

    for repo in MODEL_REPOS:
        log(f"Downloading model: {repo}...", "DL")
        run(f"""{python} -c "
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id='{repo}',
    repo_type='model',
    token='{token}'
)
print('Downloaded: {repo}')
" """)

    log("All models downloaded!", "OK")


# ═══════════════════════════════════════════════════════
# STEP 3: Set up Python venv
# ═══════════════════════════════════════════════════════

def setup_venv():
    """Create virtual environment if it doesn't exist."""
    if not os.path.exists(os.path.join(VENV_DIR, "bin", "python")):
        log("Creating virtual environment...", "INFO")
        run(f"python3 -m venv {VENV_DIR}")
    else:
        log("Virtual environment already exists.", "OK")

    # Upgrade pip
    pip = os.path.join(VENV_DIR, "bin", "pip")
    run(f"{pip} install --upgrade pip")


# ═══════════════════════════════════════════════════════
# STEP 4: Install dependencies
# ═══════════════════════════════════════════════════════

def install_dependencies():
    """Install Python packages from requirements.txt."""
    pip = os.path.join(VENV_DIR, "bin", "pip")
    req_file = os.path.join(APP_DIR, "requirements.txt")

    if os.path.exists(req_file):
        log("Installing dependencies from requirements.txt...", "DL")
        run(f"{pip} install -r {req_file}")
        log("Dependencies installed!", "OK")
    else:
        log("No requirements.txt found!", "WARN")

    # Ensure huggingface_hub is available (needed for model downloads)
    run(f"{pip} install huggingface_hub")


# ═══════════════════════════════════════════════════════
# STEP 5: Start the app
# ═══════════════════════════════════════════════════════

def start_app():
    """Start the Gradio app."""
    python = os.path.join(VENV_DIR, "bin", "python")
    app_py = os.path.join(APP_DIR, "app.py")

    if not os.path.exists(app_py):
        log("app.py not found, cannot start!", "ERR")
        return

    log("Starting the Gradio app...", "RUN")

    env_extra = {
        "PYTHONPATH": APP_DIR,
        "PYTHONUNBUFFERED": "1",
    }

    # Run in foreground so user can see output
    run(f"{python} {app_py}", cwd=APP_DIR, env_extra=env_extra, check=False)


# ═══════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════

def main():
    parser = argparse.ArgumentParser(description="Setup Qwen Image Edit on a fresh RunPod")
    parser.add_argument("--token", type=str, default=None,
                        help="HuggingFace token (or set HF_TOKEN env var)")
    parser.add_argument("--skip-models", action="store_true",
                        help="Skip model downloads (if already cached)")
    parser.add_argument("--skip-app", action="store_true",
                        help="Skip app code download (if already present)")
    parser.add_argument("--skip-deps", action="store_true",
                        help="Skip dependency installation")
    parser.add_argument("--no-start", action="store_true",
                        help="Don't auto-start the app after setup")
    args = parser.parse_args()

    # Get token
    token = args.token or os.environ.get("HF_TOKEN", "")
    if not token:
        log("No HuggingFace token provided!", "ERR")
        log("Set HF_TOKEN environment variable or pass --token", "INFO")
        sys.exit(1)

    print("=" * 60)
    print("  Qwen Image Edit — Fresh RunPod Setup")
    print("=" * 60)
    start_time = time.time()

    # Step 0: Create directories
    log("Creating directories...")
    ensure_dirs()

    # Step 1: Set up venv (needed before downloading, since we use huggingface_hub)
    log("Setting up Python virtual environment...")
    setup_venv()

    # Step 2: Install huggingface_hub first (needed for downloads)
    pip = os.path.join(VENV_DIR, "bin", "pip")
    run(f"{pip} install huggingface_hub")

    # Step 3: Download app code
    if not args.skip_app:
        download_app_code(token)
    else:
        log("Skipping app code download (--skip-app)", "INFO")

    # Step 4: Install full dependencies
    if not args.skip_deps:
        install_dependencies()
    else:
        log("Skipping dependency installation (--skip-deps)", "INFO")

    # Step 5: Download models
    if not args.skip_models:
        download_models(token)
    else:
        log("Skipping model downloads (--skip-models)", "INFO")

    elapsed = time.time() - start_time
    minutes = int(elapsed // 60)
    seconds = int(elapsed % 60)

    print("")
    print("=" * 60)
    log(f"Setup completed in {minutes}m {seconds}s!", "OK")
    print("=" * 60)

    # Step 6: Start the app
    if not args.no_start:
        start_app()
    else:
        log("Skipping app start (--no-start). To start manually:", "INFO")
        log(f"  bash /workspace/start_app.sh", "INFO")


if __name__ == "__main__":
    main()