From 80848f3c546049014d076318b04828d950af88ba Mon Sep 17 00:00:00 2001 From: Bahadir Ciloglu Date: Sat, 1 Nov 2025 17:25:26 +0300 Subject: [PATCH] feat: Add RunPod Hub configuration and storage access test - Add .runpod/hub.json with serverless configuration - Add .runpod/tests.json with comprehensive test cases - Add storage access test to Dockerfile build process - Add RunPod badge to README.md - Include model download script for build-time optimization - Test storage accessibility during Docker build phase --- .runpod/hub.json | 66 ++++++++ .runpod/tests.json | 94 +++++++++++ Dockerfile | 23 +++ README.md | 3 + debug_runpod.sh | 17 ++ download_models.py | 90 +++++++++++ runpod_template_config.md | 0 runpod_test_commands.md | 91 +++++++++++ start_runpod.py | 11 +- test-storage-access.Dockerfile | 40 +++++ test_build.sh | 21 +++ test_curl_runpod.sh | 155 +++++++++++++++++++ test_image_generation.py | 274 +++++++++++++++++++++++++++++++++ test_runpod_api.py | 257 +++++++++++++++++++++++++++++++ test_simple_workflow.json | 59 +++++++ 15 files changed, 1196 insertions(+), 5 deletions(-) create mode 100644 .runpod/hub.json create mode 100644 .runpod/tests.json create mode 100755 debug_runpod.sh create mode 100644 download_models.py create mode 100644 runpod_template_config.md create mode 100644 runpod_test_commands.md create mode 100644 test-storage-access.Dockerfile create mode 100755 test_build.sh create mode 100755 test_curl_runpod.sh create mode 100644 test_image_generation.py create mode 100644 test_runpod_api.py create mode 100644 test_simple_workflow.json diff --git a/.runpod/hub.json b/.runpod/hub.json new file mode 100644 index 000000000..97d098f39 --- /dev/null +++ b/.runpod/hub.json @@ -0,0 +1,66 @@ +{ + "title": "ComfyUI - AI Image Generation", + "description": "ComfyUI serverless deployment with network storage support for models. Generate high-quality images using Stable Diffusion XL with custom workflows.", + "type": "serverless", + "category": "image", + "iconUrl": "https://raw.githubusercontent.com/comfyanonymous/ComfyUI/master/web/favicon.ico", + "config": { + "runsOn": "GPU", + "containerDiskInGb": 20, + "presets": [ + { + "name": "SDXL Base", + "defaults": { + "MODEL_NAME": "sd_xl_base_1.0.safetensors", + "STEPS": "25", + "CFG": "8.0" + } + }, + { + "name": "High Quality", + "defaults": { + "MODEL_NAME": "sd_xl_base_1.0.safetensors", + "STEPS": "50", + "CFG": "7.0" + } + } + ], + "env": [ + { + "key": "HF_HUB_DISABLE_TELEMETRY", + "value": "1" + }, + { + "key": "DO_NOT_TRACK", + "value": "1" + }, + { + "key": "MODEL_NAME", + "input": { + "name": "Model Name", + "type": "string", + "description": "Checkpoint model file name to use", + "default": "sd_xl_base_1.0.safetensors" + } + }, + { + "key": "STEPS", + "input": { + "name": "Generation Steps", + "type": "string", + "description": "Number of denoising steps", + "default": "25" + } + }, + { + "key": "CFG", + "input": { + "name": "CFG Scale", + "type": "string", + "description": "Classifier-free guidance scale", + "default": "8.0" + } + } + ] + } +} \ No newline at end of file diff --git a/.runpod/tests.json b/.runpod/tests.json new file mode 100644 index 000000000..8407c3b87 --- /dev/null +++ b/.runpod/tests.json @@ -0,0 +1,94 @@ +{ + "tests": [ + { + "name": "basic_text_to_image", + "input": { + "workflow": { + "3": { + "inputs": { + "seed": 42, + "steps": 20, + "cfg": 8.0, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1.0, + "model": ["4", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": "sd_xl_base_1.0.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "5": { + "inputs": { + "width": 1024, + "height": 1024, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": "a beautiful landscape, high quality, detailed", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "blurry, low quality, distorted", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": ["3", 0], + "vae": ["4", 2] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "test_output", + "images": ["8", 0] + }, + "class_type": "SaveImage" + } + } + }, + "timeout": 120000 + }, + { + "name": "simple_prompt_test", + "input": { + "prompt": "modern software interface, clean design" + }, + "timeout": 60000 + } + ], + "config": { + "gpuTypeId": "NVIDIA RTX A4000", + "gpuCount": 1, + "env": [ + { + "key": "HF_HUB_DISABLE_TELEMETRY", + "value": "1" + }, + { + "key": "DO_NOT_TRACK", + "value": "1" + } + ], + "allowedCudaVersions": [ + "12.1", + "11.8" + ] + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 9f6eba161..9d2982c7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,29 @@ RUN apt-get update && apt-get install -y \ # Tüm dosyaları kopyala COPY . /app +# Model klasörlerini oluştur +RUN mkdir -p /app/models/checkpoints /app/models/loras /app/models/vae \ + /app/models/controlnet /app/models/upscale_models \ + /app/models/text_encoders /app/models/clip \ + /app/models/diffusion_models /app/models/unet \ + /app/models/embeddings /app/models/clip_vision + +# Storage erişim testi - Build sırasında storage erişimi kontrol et +RUN echo "🔍 RunPod Build Storage Access Test" && \ + echo "=================================" && \ + echo "📁 Testing storage paths:" && \ + (ls -la /runpod-volume 2>/dev/null && echo "✅ /runpod-volume accessible" || echo "❌ /runpod-volume not accessible") && \ + (ls -la /workspace 2>/dev/null && echo "✅ /workspace accessible" || echo "❌ /workspace not accessible") && \ + (ls -la /content 2>/dev/null && echo "✅ /content accessible" || echo "❌ /content not accessible") && \ + echo "🌐 Network test:" && \ + (ping -c 1 google.com >/dev/null 2>&1 && echo "✅ Internet access available" || echo "❌ No internet access") && \ + echo "=================================" + +# Temel modelleri Docker build sırasında indir +RUN pip install huggingface-hub && \ + python download_models.py && \ + rm -rf /tmp/hf_cache + # PyTorch CPU versiyonu ve bağımlılıklar RUN pip install --no-cache-dir \ torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu && \ diff --git a/README.md b/README.md index 4204777e9..f6f57681d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Dynamic JSON Badge][discord-shield]][discord-url] [![Twitter][twitter-shield]][twitter-url] [![Matrix][matrix-shield]][matrix-url] +[![Runpod][runpod-shield]][runpod-url]
[![][github-release-shield]][github-release-link] [![][github-release-date-shield]][github-release-link] @@ -18,6 +19,8 @@ [matrix-url]: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org [website-shield]: https://img.shields.io/badge/ComfyOrg-4285F4?style=flat [website-url]: https://www.comfy.org/ +[runpod-shield]: https://api.runpod.io/badge/bahadirciloglu/ComfyUI +[runpod-url]: https://console.runpod.io/hub/bahadirciloglu/ComfyUI [discord-shield]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Fcomfyorg%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&logo=discord&logoColor=white&label=Discord&color=green&suffix=%20total [discord-url]: https://www.comfy.org/discord diff --git a/debug_runpod.sh b/debug_runpod.sh new file mode 100755 index 000000000..0619734bb --- /dev/null +++ b/debug_runpod.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Debug RunPod API call + +ENDPOINT_ID="sfkzjudvrj50yq" +API_KEY="${RUNPOD_API_KEY:-YOUR_API_KEY}" +BASE_URL="https://api.runpod.ai/v2" + +echo "🔍 Debugging RunPod API..." +echo "📍 Endpoint: $ENDPOINT_ID" +echo "🔑 API Key: ${API_KEY:0:10}..." # Show only first 10 chars for security + +# Simple test with minimal payload +echo "📤 Testing with simple payload..." +curl -v -X POST "$BASE_URL/$ENDPOINT_ID/run" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $API_KEY" \ + -d '{"input":{"prompt":"test prompt"}}' \ No newline at end of file diff --git a/download_models.py b/download_models.py new file mode 100644 index 000000000..764172b8c --- /dev/null +++ b/download_models.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +""" +Docker build sırasında temel modelleri indir +""" + +import os +import sys +from pathlib import Path +from huggingface_hub import hf_hub_download + +def download_model(repo_id, filename, target_dir): + """Model indir ve hedef dizine kopyala""" + try: + print(f"📥 İndiriliyor: {repo_id}/{filename}") + + # Model'i indir + model_path = hf_hub_download( + repo_id=repo_id, + filename=filename, + cache_dir="/tmp/hf_cache" + ) + + # Hedef dizini oluştur + os.makedirs(target_dir, exist_ok=True) + + # Dosyayı kopyala + target_path = os.path.join(target_dir, filename) + os.system(f"cp '{model_path}' '{target_path}'") + + print(f"✅ Kaydedildi: {target_path}") + return True + + except Exception as e: + print(f"❌ Hata: {repo_id}/{filename} - {e}") + return False + +def main(): + """Temel modelleri indir""" + print("🚀 Docker build - Model indirme başlatılıyor...") + + models_base = "/app/models" + + # İndirilecek modeller + models_to_download = [ + # SDXL Base Model + { + "repo_id": "stabilityai/stable-diffusion-xl-base-1.0", + "filename": "sd_xl_base_1.0.safetensors", + "target_dir": f"{models_base}/checkpoints" + }, + # SDXL VAE + { + "repo_id": "stabilityai/sdxl-vae", + "filename": "sdxl_vae.safetensors", + "target_dir": f"{models_base}/vae" + }, + # CLIP Text Encoder + { + "repo_id": "openai/clip-vit-large-patch14", + "filename": "pytorch_model.bin", + "target_dir": f"{models_base}/clip" + } + ] + + success_count = 0 + + for model in models_to_download: + if download_model( + model["repo_id"], + model["filename"], + model["target_dir"] + ): + success_count += 1 + + print(f"\n🎉 Model indirme tamamlandı: {success_count}/{len(models_to_download)}") + + # Model klasörlerini listele + print("\n📁 Model klasörleri:") + for root, dirs, files in os.walk(models_base): + level = root.replace(models_base, '').count(os.sep) + indent = ' ' * 2 * level + print(f"{indent}{os.path.basename(root)}/") + subindent = ' ' * 2 * (level + 1) + for file in files: + file_size = os.path.getsize(os.path.join(root, file)) + size_mb = file_size / (1024 * 1024) + print(f"{subindent}{file} ({size_mb:.1f} MB)") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/runpod_template_config.md b/runpod_template_config.md new file mode 100644 index 000000000..e69de29bb diff --git a/runpod_test_commands.md b/runpod_test_commands.md new file mode 100644 index 000000000..7e642ebf4 --- /dev/null +++ b/runpod_test_commands.md @@ -0,0 +1,91 @@ +# RunPod Container Test Komutları + +## 1. Container'a Bağlan +RunPod dashboard'da "Connect" → "Start Web Terminal" + +## 2. Test Scriptini Çalıştır +```bash +# Container içinde: +cd /app +python test_image_generation.py +``` + +## 3. Manuel Test (Alternatif) +```bash +# ComfyUI durumunu kontrol et +curl http://127.0.0.1:8188/system_stats + +# Model listesini al +curl http://127.0.0.1:8188/object_info | jq '.CheckpointLoaderSimple.input.required.ckpt_name' + +# Basit workflow gönder +curl -X POST http://127.0.0.1:8188/prompt \ + -H "Content-Type: application/json" \ + -d @test_simple_workflow.json +``` + +## 4. Network Storage Kontrolü +```bash +# Models klasörünü kontrol et +ls -la /app/models/ +ls -la /runpod-volume/models/ + +# Symlink kontrolü +readlink /app/models +``` + +## 5. Model Yükleme (Gerekirse) +```bash +# Network storage'a model yükle +cd /runpod-volume/models/checkpoints +wget https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors + +# ComfyUI'yi yeniden başlat +pkill -f main.py +python main.py --listen 0.0.0.0 --port 8188 --cpu +``` + +## 6. Beklenen Çıktılar + +### Başarılı Durum: +``` +✅ ComfyUI server çalışıyor +✅ Yüklü modeller (1 adet): + - sd_xl_base_1.0.safetensors +🎨 Resim üretiliyor... + Model: sd_xl_base_1.0.safetensors + Prompt: a modern user interface design... +📝 Prompt ID: abc123 +⏳ Bekleniyor... (15s) +✅ Resim kaydedildi: test_output/test_ui_design_00001_.png +🎉 Test tamamlandı! +``` + +### Hata Durumu: +``` +❌ ComfyUI server'a bağlanılamıyor +❌ Hiç model bulunamadı +❌ Network storage mount edilmedi +``` + +## 7. Troubleshooting + +### ComfyUI Başlamazsa: +```bash +# Logları kontrol et +tail -f /var/log/comfyui.log + +# Manuel başlat +cd /app +python main.py --listen 0.0.0.0 --port 8188 --cpu +``` + +### Network Storage Sorunları: +```bash +# Mount durumunu kontrol et +mount | grep runpod-volume + +# Manuel mount +mkdir -p /runpod-volume/models +ln -sf /runpod-volume/models /app/models +``` \ No newline at end of file diff --git a/start_runpod.py b/start_runpod.py index fe0a42587..b580685e2 100644 --- a/start_runpod.py +++ b/start_runpod.py @@ -161,11 +161,12 @@ def main(): # Environment setup setup_environment() - # Network storage'ı bekle - wait_for_storage() - - # Network storage mount - mount_runpod_storage() + # Hızlı başlatma için network storage mount'u atla + # Modeller zaten Docker image'da mevcut + logger.info("Hızlı başlatma modu - modeller image'da mevcut") + + # Model klasörlerini kontrol et + ensure_local_model_folders() # Temel modelleri indir (opsiyonel) download_essential_models() diff --git a/test-storage-access.Dockerfile b/test-storage-access.Dockerfile new file mode 100644 index 000000000..510708a5a --- /dev/null +++ b/test-storage-access.Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.10-slim + +# RunPod build sırasında storage erişimi test et +RUN echo "🔍 RunPod Build Storage Access Test" && \ + echo "=================================" && \ + echo "" && \ + echo "📁 Testing common RunPod storage paths:" && \ + echo "" && \ + echo "1. /workspace:" && \ + (ls -la /workspace 2>/dev/null || echo "❌ /workspace not accessible") && \ + echo "" && \ + echo "2. /runpod-volume:" && \ + (ls -la /runpod-volume 2>/dev/null || echo "❌ /runpod-volume not accessible") && \ + echo "" && \ + echo "3. /content:" && \ + (ls -la /content 2>/dev/null || echo "❌ /content not accessible") && \ + echo "" && \ + echo "4. /storage:" && \ + (ls -la /storage 2>/dev/null || echo "❌ /storage not accessible") && \ + echo "" && \ + echo "5. Environment variables:" && \ + env | grep -i runpod || echo "❌ No RUNPOD env vars found" && \ + echo "" && \ + echo "6. Mount points:" && \ + mount | grep -E "(workspace|runpod|storage)" || echo "❌ No storage mounts found" && \ + echo "" && \ + echo "7. Available disk space:" && \ + df -h && \ + echo "" && \ + echo "8. Network connectivity test:" && \ + (ping -c 1 google.com >/dev/null 2>&1 && echo "✅ Internet access available" || echo "❌ No internet access") && \ + echo "" && \ + echo "=================================" && \ + echo "🏁 Test completed" + +# Basit bir uygulama +WORKDIR /app +RUN echo 'print("Hello from RunPod build test!")' > app.py + +CMD ["python", "app.py"] \ No newline at end of file diff --git a/test_build.sh b/test_build.sh new file mode 100755 index 000000000..ce8c36743 --- /dev/null +++ b/test_build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# RunPod Build Test Script + +echo "🔍 RunPod Storage Access Test" +echo "=============================" + +# Docker build ile test et (local) +echo "📦 Local Docker build test..." +docker build -f test-storage-access.Dockerfile -t runpod-storage-test . 2>&1 | grep -E "(Storage Access Test|accessible|not accessible)" + +echo "" +echo "🚀 Gerçek test için RunPod Dashboard'da bu Dockerfile'ı kullan:" +echo " 1. RunPod Dashboard → Templates → Create Template" +echo " 2. Container Image → Build from Dockerfile" +echo " 3. test-storage-access.Dockerfile içeriğini yapıştır" +echo " 4. Build loglarında storage erişim sonuçlarını kontrol et" + +echo "" +echo "📋 Test sonuçlarına göre:" +echo " ✅ Storage erişimi VARSA → Build sırasında model kopyalama" +echo " ❌ Storage erişimi YOKSA → HuggingFace'den model indirme" \ No newline at end of file diff --git a/test_curl_runpod.sh b/test_curl_runpod.sh new file mode 100755 index 000000000..dc085b6c8 --- /dev/null +++ b/test_curl_runpod.sh @@ -0,0 +1,155 @@ +#!/bin/bash +# RunPod Serverless API Test with curl +# Usage: ./test_curl_runpod.sh "your prompt here" + +# Configuration +ENDPOINT_ID="sfkzjudvrj50yq" +API_KEY="${RUNPOD_API_KEY:-YOUR_API_KEY}" # Set RUNPOD_API_KEY environment variable +BASE_URL="https://api.runpod.ai/v2" + +# Check if API key is set +if [ "$API_KEY" = "YOUR_API_KEY" ]; then + echo "❌ API key not set!" + echo "💡 Set your API key: export RUNPOD_API_KEY='your_key_here'" + echo " Or edit this script and replace YOUR_API_KEY with your actual key" + exit 1 +fi + +# Get prompt from command line or use default +PROMPT="${1:-modern software interface, clean dashboard design, professional UI layout, high quality, detailed}" + +echo "🚀 Testing RunPod Serverless API..." +echo "📍 Endpoint: $ENDPOINT_ID" +echo "💬 Prompt: $PROMPT" + +# Create the JSON payload +JSON_PAYLOAD=$(cat </dev/null || echo "$STATUS_RESPONSE" + exit 0 + ;; + "FAILED") + echo "❌ Job failed!" + echo "📄 Error response:" + echo "$STATUS_RESPONSE" | jq '.' 2>/dev/null || echo "$STATUS_RESPONSE" + exit 1 + ;; + "IN_QUEUE"|"IN_PROGRESS") + echo "⏳ Job status: $STATUS (${WAIT_TIME}s elapsed)" + ;; + *) + echo "❓ Unknown status: $STATUS" + ;; + esac + + sleep 5 + WAIT_TIME=$((WAIT_TIME + 5)) +done + +echo "⏰ Timeout after ${MAX_WAIT} seconds" +echo "📄 Last status response:" +echo "$STATUS_RESPONSE" | jq '.' 2>/dev/null || echo "$STATUS_RESPONSE" \ No newline at end of file diff --git a/test_image_generation.py b/test_image_generation.py new file mode 100644 index 000000000..5c7ba7a57 --- /dev/null +++ b/test_image_generation.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +""" +ComfyUI Test Script - pars klasöründeki resimlere benzer resim üretimi +""" + +import os +import json +import base64 +import requests +from pathlib import Path +import time + +class ComfyUITester: + def __init__(self, comfyui_url="http://127.0.0.1:8188"): + self.comfyui_url = comfyui_url + self.client_id = "test_client" + + def check_server(self): + """ComfyUI server'ın çalışıp çalışmadığını kontrol et""" + try: + response = requests.get(f"{self.comfyui_url}/system_stats", timeout=5) + if response.status_code == 200: + print("✅ ComfyUI server çalışıyor") + return True + else: + print(f"❌ ComfyUI server yanıt vermiyor: {response.status_code}") + return False + except requests.exceptions.RequestException as e: + print(f"❌ ComfyUI server'a bağlanılamıyor: {e}") + return False + + def get_models(self): + """Yüklü modelleri listele""" + try: + response = requests.get(f"{self.comfyui_url}/object_info") + if response.status_code == 200: + data = response.json() + checkpoints = data.get("CheckpointLoaderSimple", {}).get("input", {}).get("required", {}).get("ckpt_name", []) + if isinstance(checkpoints, list) and len(checkpoints) > 1: + models = checkpoints[0] # İlk element model listesi + print(f"✅ Yüklü modeller ({len(models)} adet):") + for model in models[:5]: # İlk 5 modeli göster + print(f" - {model}") + return models + else: + print("❌ Model listesi alınamadı") + return [] + except Exception as e: + print(f"❌ Model listesi alınamadı: {e}") + return [] + + def create_basic_workflow(self, model_name="sd_xl_base_1.0.safetensors", prompt="a beautiful landscape"): + """Temel SDXL workflow oluştur""" + workflow = { + "3": { + "inputs": { + "seed": 42, + "steps": 20, + "cfg": 8.0, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1.0, + "model": ["4", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": model_name + }, + "class_type": "CheckpointLoaderSimple" + }, + "5": { + "inputs": { + "width": 1024, + "height": 1024, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": prompt, + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "blurry, low quality, distorted", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": ["3", 0], + "vae": ["4", 2] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "test_output", + "images": ["8", 0] + }, + "class_type": "SaveImage" + } + } + return workflow + + def generate_image(self, prompt, model_name=None): + """Resim üret""" + try: + # Model seç + if not model_name: + models = self.get_models() + if not models: + print("❌ Hiç model bulunamadı") + return None + model_name = models[0] # İlk modeli kullan + + print(f"🎨 Resim üretiliyor...") + print(f" Model: {model_name}") + print(f" Prompt: {prompt}") + + # Workflow oluştur + workflow = self.create_basic_workflow(model_name, prompt) + + # İsteği gönder + response = requests.post( + f"{self.comfyui_url}/prompt", + json={ + "prompt": workflow, + "client_id": self.client_id + }, + timeout=30 + ) + response.raise_for_status() + + prompt_id = response.json()["prompt_id"] + print(f"📝 Prompt ID: {prompt_id}") + + # Tamamlanmasını bekle + return self.wait_for_completion(prompt_id) + + except Exception as e: + print(f"❌ Resim üretimi başarısız: {e}") + return None + + def wait_for_completion(self, prompt_id, timeout=300): + """İşlemin tamamlanmasını bekle""" + start_time = time.time() + + while time.time() - start_time < timeout: + try: + # Queue durumunu kontrol et + queue_response = requests.get(f"{self.comfyui_url}/queue") + queue_data = queue_response.json() + + # İşlem hala çalışıyor mu? + running = any(item[1]["prompt_id"] == prompt_id for item in queue_data.get("queue_running", [])) + pending = any(item[1]["prompt_id"] == prompt_id for item in queue_data.get("queue_pending", [])) + + if not running and not pending: + # İşlem tamamlandı, sonuçları al + history_response = requests.get(f"{self.comfyui_url}/history/{prompt_id}") + if history_response.status_code == 200: + history_data = history_response.json() + if prompt_id in history_data: + return self.download_results(history_data[prompt_id]) + + print(f"⏳ Bekleniyor... ({int(time.time() - start_time)}s)") + time.sleep(3) + + except Exception as e: + print(f"❌ Durum kontrolü hatası: {e}") + time.sleep(5) + + print(f"⏰ Timeout: {timeout} saniye") + return None + + def download_results(self, history_data): + """Sonuçları indir""" + results = [] + + if "outputs" in history_data: + for node_id, node_output in history_data["outputs"].items(): + if "images" in node_output: + for image_info in node_output["images"]: + try: + # Resmi indir + image_url = f"{self.comfyui_url}/view" + params = { + "filename": image_info["filename"], + "subfolder": image_info.get("subfolder", ""), + "type": image_info.get("type", "output") + } + + image_response = requests.get(image_url, params=params) + image_response.raise_for_status() + + # Test output klasörüne kaydet + os.makedirs("test_output", exist_ok=True) + output_path = f"test_output/{image_info['filename']}" + + with open(output_path, "wb") as f: + f.write(image_response.content) + + results.append({ + "filename": image_info["filename"], + "path": output_path, + "node_id": node_id + }) + + print(f"✅ Resim kaydedildi: {output_path}") + + except Exception as e: + print(f"❌ Resim indirme hatası: {e}") + + return results + +def main(): + """Ana test fonksiyonu""" + print("🚀 ComfyUI Test Başlatılıyor...") + + tester = ComfyUITester() + + # Server kontrolü + if not tester.check_server(): + print("\n💡 ComfyUI'yi başlatmak için:") + print(" python main.py --listen 0.0.0.0 --port 8188") + return + + # Model kontrolü + models = tester.get_models() + if not models: + print("\n💡 Model yüklemek için:") + print(" 1. models/checkpoints/ klasörüne .safetensors dosyası ekleyin") + print(" 2. ComfyUI'yi yeniden başlatın") + return + + # Test prompt'ları (pars klasöründeki screenshot'lara benzer) + test_prompts = [ + "a modern user interface design, clean layout, professional software interface", + "screenshot of a web application, dashboard design, modern UI elements", + "software interface mockup, clean design, professional layout", + "application screenshot, user interface design, modern web app", + "dashboard interface, clean UI design, professional software layout" + ] + + print(f"\n🎨 {len(test_prompts)} adet test resmi üretiliyor...") + + # Her prompt için resim üret + for i, prompt in enumerate(test_prompts, 1): + print(f"\n--- Test {i}/{len(test_prompts)} ---") + result = tester.generate_image(prompt, models[0]) + + if result: + print(f"✅ Test {i} başarılı: {len(result)} resim üretildi") + else: + print(f"❌ Test {i} başarısız") + + # Kısa bekleme + time.sleep(2) + + print("\n🎉 Test tamamlandı!") + print("📁 Üretilen resimler: test_output/ klasöründe") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_runpod_api.py b/test_runpod_api.py new file mode 100644 index 000000000..2919832db --- /dev/null +++ b/test_runpod_api.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python3 +""" +RunPod Serverless API Test - pars klasöründeki resimlere benzer resim üretimi +""" + +import os +import json +import base64 +import requests +import time +from pathlib import Path + +class RunPodTester: + def __init__(self): + # RunPod API bilgileri (.env.example'dan alın) + self.api_key = os.getenv("RUNPOD_API_KEY", "YOUR_RUNPOD_API_KEY") # .env dosyasından alın + self.endpoint_id = "sfkzjudvrj50yq" # Dashboard'dan aldığınız ID + self.base_url = "https://api.runpod.ai/v2" + + def create_test_workflow(self, prompt): + """Test workflow'u oluştur""" + return { + "input": { + "workflow": { + "3": { + "inputs": { + "seed": 42, + "steps": 25, + "cfg": 8.0, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1.0, + "model": ["4", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": "sd_xl_base_1.0.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "5": { + "inputs": { + "width": 1024, + "height": 1024, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": prompt, + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "blurry, low quality, distorted, ugly, bad anatomy, worst quality", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": ["3", 0], + "vae": ["4", 2] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "runpod_test", + "images": ["8", 0] + }, + "class_type": "SaveImage" + } + } + } + } + + def submit_job(self, prompt): + """RunPod'a job gönder""" + try: + workflow_data = self.create_test_workflow(prompt) + + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + url = f"{self.base_url}/{self.endpoint_id}/run" + + print(f"🚀 Job gönderiliyor...") + print(f" Endpoint: {self.endpoint_id}") + print(f" Prompt: {prompt[:50]}...") + + response = requests.post(url, json=workflow_data, headers=headers, timeout=30) + response.raise_for_status() + + job_data = response.json() + job_id = job_data.get("id") + + print(f"✅ Job gönderildi: {job_id}") + return job_id + + except Exception as e: + print(f"❌ Job gönderme hatası: {e}") + return None + + def check_job_status(self, job_id): + """Job durumunu kontrol et""" + try: + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + url = f"{self.base_url}/{self.endpoint_id}/status/{job_id}" + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + + return response.json() + + except Exception as e: + print(f"❌ Durum kontrolü hatası: {e}") + return None + + def wait_for_completion(self, job_id, timeout=300): + """Job'ın tamamlanmasını bekle""" + start_time = time.time() + + while time.time() - start_time < timeout: + status_data = self.check_job_status(job_id) + + if not status_data: + time.sleep(5) + continue + + status = status_data.get("status") + + if status == "COMPLETED": + print("✅ Job tamamlandı!") + return status_data.get("output") + elif status == "FAILED": + print("❌ Job başarısız!") + print(f" Hata: {status_data.get('error', 'Bilinmeyen hata')}") + return None + elif status in ["IN_QUEUE", "IN_PROGRESS"]: + elapsed = int(time.time() - start_time) + print(f"⏳ Job çalışıyor... ({status}) - {elapsed}s") + + time.sleep(5) + + print(f"⏰ Timeout: {timeout} saniye") + return None + + def save_results(self, output_data, test_name): + """Sonuçları kaydet""" + if not output_data: + return [] + + os.makedirs("test_output", exist_ok=True) + saved_files = [] + + outputs = output_data.get("outputs", []) + for i, output in enumerate(outputs): + if "data" in output: + try: + # Base64 decode + image_data = base64.b64decode(output["data"]) + + # Dosya adı + filename = f"{test_name}_{i+1:02d}.png" + filepath = f"test_output/{filename}" + + # Kaydet + with open(filepath, "wb") as f: + f.write(image_data) + + saved_files.append(filepath) + print(f"💾 Resim kaydedildi: {filepath}") + + except Exception as e: + print(f"❌ Resim kaydetme hatası: {e}") + + return saved_files + +def main(): + """Ana test fonksiyonu""" + print("🚀 RunPod Serverless Test Başlatılıyor...") + + tester = RunPodTester() + + # API key kontrolü + if tester.api_key == "YOUR_RUNPOD_API_KEY": + print("❌ API key ayarlanmamış!") + print("💡 test_runpod_api.py dosyasında API key'inizi güncelleyin") + print(" API key'i RunPod dashboard → Settings → API Keys'den alabilirsiniz") + return + + # pars klasöründeki screenshot'lara benzer prompt'lar + test_prompts = [ + "modern software interface, clean dashboard design, professional UI layout, high quality, detailed", + "web application screenshot, user interface mockup, modern design system, clean layout", + "software dashboard, data visualization interface, professional web app design, modern UI", + "application interface design, clean user experience, professional software layout, modern style", + "digital interface mockup, clean web design, professional dashboard layout, modern UI elements" + ] + + print(f"\n🎨 {len(test_prompts)} adet test resmi üretiliyor...") + print(f"📍 Endpoint: {tester.endpoint_id}") + + all_results = [] + + # Her prompt için test + for i, prompt in enumerate(test_prompts, 1): + print(f"\n--- Test {i}/{len(test_prompts)} ---") + + # Job gönder + job_id = tester.submit_job(prompt) + if not job_id: + continue + + # Tamamlanmasını bekle + output = tester.wait_for_completion(job_id) + + # Sonuçları kaydet + if output: + saved_files = tester.save_results(output, f"ui_design_test_{i}") + all_results.extend(saved_files) + print(f"✅ Test {i} başarılı: {len(saved_files)} resim") + else: + print(f"❌ Test {i} başarısız") + + # Rate limiting için bekleme + if i < len(test_prompts): + print("⏸️ Rate limiting için 10 saniye bekleniyor...") + time.sleep(10) + + # Özet + print(f"\n🎉 Test tamamlandı!") + print(f"📊 Toplam üretilen resim: {len(all_results)}") + print(f"📁 Kaydedilen dosyalar:") + for file in all_results: + print(f" - {file}") + + if all_results: + print(f"\n💡 Resimleri görüntülemek için:") + print(f" open test_output/") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_simple_workflow.json b/test_simple_workflow.json new file mode 100644 index 000000000..5e981f3b3 --- /dev/null +++ b/test_simple_workflow.json @@ -0,0 +1,59 @@ +{ + "3": { + "inputs": { + "seed": 42, + "steps": 20, + "cfg": 8.0, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1.0, + "model": ["4", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": "sd_xl_base_1.0.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "5": { + "inputs": { + "width": 1024, + "height": 1024, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": "a modern user interface design, clean layout, professional software interface, high quality, detailed", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "blurry, low quality, distorted, ugly, bad anatomy", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": ["3", 0], + "vae": ["4", 2] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "test_ui_design", + "images": ["8", 0] + }, + "class_type": "SaveImage" + } +} \ No newline at end of file