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
This commit is contained in:
Bahadir Ciloglu 2025-11-01 17:25:26 +03:00
parent cd8bb55239
commit 80848f3c54
15 changed files with 1196 additions and 5 deletions

66
.runpod/hub.json Normal file
View File

@ -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"
}
}
]
}
}

94
.runpod/tests.json Normal file
View File

@ -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"
]
}
}

View File

@ -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 && \

View File

@ -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]
<br>
[![][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
<!-- Workaround to display total user from https://github.com/badges/shields/issues/4500#issuecomment-2060079995 -->
[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

17
debug_runpod.sh Executable file
View File

@ -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"}}'

90
download_models.py Normal file
View File

@ -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()

View File

91
runpod_test_commands.md Normal file
View File

@ -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
```

View File

@ -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()

View File

@ -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"]

21
test_build.sh Executable file
View File

@ -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"

155
test_curl_runpod.sh Executable file
View File

@ -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 <<EOF
{
"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": "curl_test",
"images": ["8", 0]
},
"class_type": "SaveImage"
}
}
}
}
EOF
)
# Submit the job
echo "📤 Submitting job..."
RESPONSE=$(curl -s -X POST "$BASE_URL/$ENDPOINT_ID/run" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d "$JSON_PAYLOAD")
# Check if curl succeeded
if [ $? -ne 0 ]; then
echo "❌ Curl command failed"
exit 1
fi
# Parse job ID
JOB_ID=$(echo "$RESPONSE" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
if [ -z "$JOB_ID" ]; then
echo "❌ Failed to get job ID"
echo "Response: $RESPONSE"
exit 1
fi
echo "✅ Job submitted: $JOB_ID"
# Monitor job status
echo "⏳ Monitoring job status..."
MAX_WAIT=300 # 5 minutes
WAIT_TIME=0
while [ $WAIT_TIME -lt $MAX_WAIT ]; do
STATUS_RESPONSE=$(curl -s -X GET "$BASE_URL/$ENDPOINT_ID/status/$JOB_ID" \
-H "Authorization: Bearer $API_KEY")
STATUS=$(echo "$STATUS_RESPONSE" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
case "$STATUS" in
"COMPLETED")
echo "✅ Job completed!"
echo "📄 Full response:"
echo "$STATUS_RESPONSE" | jq '.' 2>/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"

274
test_image_generation.py Normal file
View File

@ -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()

257
test_runpod_api.py Normal file
View File

@ -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()

59
test_simple_workflow.json Normal file
View File

@ -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"
}
}