Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -24,7 +24,6 @@ import time
|
|
| 24 |
from requests.adapters import HTTPAdapter
|
| 25 |
from urllib3.util.retry import Retry
|
| 26 |
from huggingface_hub import HfApi
|
| 27 |
-
import accelerate
|
| 28 |
import sys
|
| 29 |
import traceback
|
| 30 |
|
|
@@ -38,7 +37,7 @@ print(f"Python version: {sys.version}")
|
|
| 38 |
print(f"PyTorch version: {torch.__version__}")
|
| 39 |
print(f"CUDA available: {torch.cuda.is_available()}")
|
| 40 |
|
| 41 |
-
#
|
| 42 |
app = FastAPI(title="Storybook Generator API")
|
| 43 |
|
| 44 |
@app.get("/ping")
|
|
@@ -47,7 +46,7 @@ async def ping():
|
|
| 47 |
return {
|
| 48 |
"status": "alive",
|
| 49 |
"timestamp": datetime.now().isoformat(),
|
| 50 |
-
"message": "
|
| 51 |
}
|
| 52 |
|
| 53 |
@app.get("/debug")
|
|
@@ -73,22 +72,6 @@ app.add_middleware(
|
|
| 73 |
allow_headers=["*"],
|
| 74 |
)
|
| 75 |
|
| 76 |
-
# =============================================
|
| 77 |
-
# MEMORY OPTIMIZATION SETTINGS
|
| 78 |
-
# =============================================
|
| 79 |
-
# Check if CUDA is available and set memory optimization
|
| 80 |
-
if torch.cuda.is_available():
|
| 81 |
-
print("✅ CUDA available, enabling GPU optimizations")
|
| 82 |
-
torch.backends.cuda.enable_flash_sdp(True)
|
| 83 |
-
torch.backends.cuda.enable_mem_efficient_sdp(True)
|
| 84 |
-
else:
|
| 85 |
-
print("⚠️ CUDA not available, running on CPU")
|
| 86 |
-
|
| 87 |
-
# Set environment variables for memory optimization
|
| 88 |
-
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"
|
| 89 |
-
os.environ["OMP_NUM_THREADS"] = "1"
|
| 90 |
-
os.environ["MKL_NUM_THREADS"] = "1"
|
| 91 |
-
|
| 92 |
# =============================================
|
| 93 |
# HUGGING FACE DATASET CONFIGURATION
|
| 94 |
# =============================================
|
|
@@ -156,7 +139,7 @@ class MemoryStatusResponse(BaseModel):
|
|
| 156 |
gpu_memory_cached_mb: Optional[float] = None
|
| 157 |
status: str
|
| 158 |
|
| 159 |
-
# HIGH-QUALITY MODEL SELECTION
|
| 160 |
MODEL_CHOICES = {
|
| 161 |
"dreamshaper-8": "lykon/dreamshaper-8",
|
| 162 |
"realistic-vision": "SG161222/Realistic_Vision_V5.1",
|
|
@@ -178,7 +161,7 @@ model_lock = threading.Lock()
|
|
| 178 |
model_loading = False
|
| 179 |
model_load_error = None
|
| 180 |
|
| 181 |
-
# MEMORY MANAGEMENT FUNCTIONS
|
| 182 |
def get_memory_usage():
|
| 183 |
"""Get current memory usage statistics"""
|
| 184 |
process = psutil.Process()
|
|
@@ -267,10 +250,10 @@ def clear_memory(clear_models=True, clear_jobs=False, clear_local_images=False,
|
|
| 267 |
}
|
| 268 |
|
| 269 |
# =============================================
|
| 270 |
-
# MODEL LOADING
|
| 271 |
# =============================================
|
| 272 |
def load_model(model_name="dreamshaper-8"):
|
| 273 |
-
"""Thread-safe model loading
|
| 274 |
global model_cache, current_model_name, current_pipe, model_loading, model_load_error
|
| 275 |
|
| 276 |
with model_lock:
|
|
@@ -279,10 +262,6 @@ def load_model(model_name="dreamshaper-8"):
|
|
| 279 |
current_model_name = model_name
|
| 280 |
return current_pipe
|
| 281 |
|
| 282 |
-
if model_loading:
|
| 283 |
-
print(f"⏳ Model already loading, waiting...")
|
| 284 |
-
return None
|
| 285 |
-
|
| 286 |
model_loading = True
|
| 287 |
model_load_error = None
|
| 288 |
|
|
@@ -290,29 +269,23 @@ def load_model(model_name="dreamshaper-8"):
|
|
| 290 |
try:
|
| 291 |
model_id = MODEL_CHOICES.get(model_name, "lykon/dreamshaper-8")
|
| 292 |
|
| 293 |
-
# Load
|
| 294 |
pipe = StableDiffusionPipeline.from_pretrained(
|
| 295 |
model_id,
|
| 296 |
torch_dtype=torch.float32,
|
| 297 |
safety_checker=None,
|
| 298 |
requires_safety_checker=False,
|
| 299 |
-
cache_dir="./model_cache"
|
| 300 |
-
low_cpu_mem_usage=True,
|
| 301 |
-
use_safetensors=True
|
| 302 |
)
|
| 303 |
|
| 304 |
-
# Use
|
| 305 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
| 306 |
|
| 307 |
-
#
|
| 308 |
-
pipe.enable_attention_slicing()
|
| 309 |
-
|
| 310 |
-
# Enable sequential CPU offload if needed
|
| 311 |
-
if not torch.cuda.is_available():
|
| 312 |
-
pipe.enable_sequential_cpu_offload()
|
| 313 |
-
|
| 314 |
pipe = pipe.to("cpu")
|
| 315 |
|
|
|
|
|
|
|
| 316 |
model_cache[model_name] = pipe
|
| 317 |
current_pipe = pipe
|
| 318 |
current_model_name = model_name
|
|
@@ -332,12 +305,9 @@ def load_model(model_name="dreamshaper-8"):
|
|
| 332 |
"runwayml/stable-diffusion-v1-5",
|
| 333 |
torch_dtype=torch.float32,
|
| 334 |
safety_checker=None,
|
| 335 |
-
requires_safety_checker=False
|
| 336 |
-
low_cpu_mem_usage=True
|
| 337 |
).to("cpu")
|
| 338 |
|
| 339 |
-
pipe.enable_attention_slicing()
|
| 340 |
-
|
| 341 |
model_cache[model_name] = pipe
|
| 342 |
current_pipe = pipe
|
| 343 |
current_model_name = "sd-1.5"
|
|
@@ -443,9 +413,9 @@ def upload_image_to_hf_dataset(image, project_id, page_number, prompt, style="")
|
|
| 443 |
print(f"❌ Failed to upload image to HF Dataset: {e}")
|
| 444 |
return None
|
| 445 |
|
| 446 |
-
# PROMPT ENGINEERING
|
| 447 |
def enhance_prompt_simple(scene_visual, style="childrens_book"):
|
| 448 |
-
"""Simple prompt enhancement"""
|
| 449 |
|
| 450 |
style_templates = {
|
| 451 |
"childrens_book": "children's book illustration, watercolor style, soft colors, whimsical, magical, storybook art, professional illustration",
|
|
@@ -466,13 +436,16 @@ def enhance_prompt_simple(scene_visual, style="childrens_book"):
|
|
| 466 |
return enhanced_prompt, negative_prompt
|
| 467 |
|
| 468 |
# =============================================
|
| 469 |
-
# IMAGE GENERATION
|
| 470 |
# =============================================
|
| 471 |
def generate_image_simple(prompt, model_choice, style, scene_number, consistency_seed=None):
|
| 472 |
-
"""Generate image
|
| 473 |
|
| 474 |
if current_pipe is None:
|
| 475 |
-
|
|
|
|
|
|
|
|
|
|
| 476 |
|
| 477 |
enhanced_prompt, negative_prompt = enhance_prompt_simple(prompt, style)
|
| 478 |
|
|
@@ -484,19 +457,16 @@ def generate_image_simple(prompt, model_choice, style, scene_number, consistency
|
|
| 484 |
try:
|
| 485 |
pipe = current_pipe
|
| 486 |
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
if torch.cuda.is_available():
|
| 499 |
-
torch.cuda.empty_cache()
|
| 500 |
|
| 501 |
print(f"✅ Generated image for scene {scene_number}")
|
| 502 |
return image
|
|
@@ -505,7 +475,7 @@ def generate_image_simple(prompt, model_choice, style, scene_number, consistency
|
|
| 505 |
print(f"❌ Generation failed: {str(e)}")
|
| 506 |
raise
|
| 507 |
|
| 508 |
-
# LOCAL FILE MANAGEMENT FUNCTIONS
|
| 509 |
def save_image_to_local(image, prompt, style="test"):
|
| 510 |
"""Save image to local persistent storage"""
|
| 511 |
try:
|
|
@@ -661,7 +631,7 @@ def calculate_remaining_time(job_id, progress):
|
|
| 661 |
|
| 662 |
# BACKGROUND TASK
|
| 663 |
def generate_storybook_background(job_id: str):
|
| 664 |
-
"""Background task
|
| 665 |
try:
|
| 666 |
if HF_TOKEN:
|
| 667 |
ensure_dataset_exists()
|
|
@@ -790,12 +760,7 @@ async def root():
|
|
| 790 |
"clear_memory": "POST /api/clear-memory",
|
| 791 |
"local_images": "GET /api/local-images"
|
| 792 |
},
|
| 793 |
-
"ui": "/ui"
|
| 794 |
-
"test_commands": {
|
| 795 |
-
"ping": "curl -X GET https://yukee1992-Video_image_generator.hf.space/ping",
|
| 796 |
-
"health": "curl -X GET https://yukee1992-Video_image_generator.hf.space/api/health",
|
| 797 |
-
"generate": "curl -X POST https://yukee1992-Video_image_generator.hf.space/api/generate-storybook -H 'Content-Type: application/json' -d '{\"story_title\":\"test\",\"scenes\":[{\"visual\":\"a cat\",\"text\":\"test\"}]}'"
|
| 798 |
-
}
|
| 799 |
}
|
| 800 |
|
| 801 |
@app.get("/api/health")
|
|
@@ -974,10 +939,11 @@ if __name__ == "__main__":
|
|
| 974 |
print("📡 API endpoints:")
|
| 975 |
print(" - GET /ping")
|
| 976 |
print(" - GET /debug")
|
|
|
|
| 977 |
print(" - GET /api/health")
|
| 978 |
print(" - POST /api/generate-storybook")
|
| 979 |
-
print(" - GET /api/job-status/{
|
| 980 |
-
print(" - GET /api/project-images/{
|
| 981 |
print(" - GET /api/memory-status")
|
| 982 |
print(" - POST /api/clear-memory")
|
| 983 |
print(" - GET /api/local-images")
|
|
|
|
| 24 |
from requests.adapters import HTTPAdapter
|
| 25 |
from urllib3.util.retry import Retry
|
| 26 |
from huggingface_hub import HfApi
|
|
|
|
| 27 |
import sys
|
| 28 |
import traceback
|
| 29 |
|
|
|
|
| 37 |
print(f"PyTorch version: {torch.__version__}")
|
| 38 |
print(f"CUDA available: {torch.cuda.is_available()}")
|
| 39 |
|
| 40 |
+
# Simple diagnostic endpoint that works immediately
|
| 41 |
app = FastAPI(title="Storybook Generator API")
|
| 42 |
|
| 43 |
@app.get("/ping")
|
|
|
|
| 46 |
return {
|
| 47 |
"status": "alive",
|
| 48 |
"timestamp": datetime.now().isoformat(),
|
| 49 |
+
"message": "API is running"
|
| 50 |
}
|
| 51 |
|
| 52 |
@app.get("/debug")
|
|
|
|
| 72 |
allow_headers=["*"],
|
| 73 |
)
|
| 74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
# =============================================
|
| 76 |
# HUGGING FACE DATASET CONFIGURATION
|
| 77 |
# =============================================
|
|
|
|
| 139 |
gpu_memory_cached_mb: Optional[float] = None
|
| 140 |
status: str
|
| 141 |
|
| 142 |
+
# HIGH-QUALITY MODEL SELECTION - SAME AS WORKING VERSION
|
| 143 |
MODEL_CHOICES = {
|
| 144 |
"dreamshaper-8": "lykon/dreamshaper-8",
|
| 145 |
"realistic-vision": "SG161222/Realistic_Vision_V5.1",
|
|
|
|
| 161 |
model_loading = False
|
| 162 |
model_load_error = None
|
| 163 |
|
| 164 |
+
# MEMORY MANAGEMENT FUNCTIONS - FROM WORKING VERSION
|
| 165 |
def get_memory_usage():
|
| 166 |
"""Get current memory usage statistics"""
|
| 167 |
process = psutil.Process()
|
|
|
|
| 250 |
}
|
| 251 |
|
| 252 |
# =============================================
|
| 253 |
+
# SIMPLIFIED MODEL LOADING - EXACTLY LIKE WORKING VERSION
|
| 254 |
# =============================================
|
| 255 |
def load_model(model_name="dreamshaper-8"):
|
| 256 |
+
"""Thread-safe model loading - simplified like working version"""
|
| 257 |
global model_cache, current_model_name, current_pipe, model_loading, model_load_error
|
| 258 |
|
| 259 |
with model_lock:
|
|
|
|
| 262 |
current_model_name = model_name
|
| 263 |
return current_pipe
|
| 264 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
model_loading = True
|
| 266 |
model_load_error = None
|
| 267 |
|
|
|
|
| 269 |
try:
|
| 270 |
model_id = MODEL_CHOICES.get(model_name, "lykon/dreamshaper-8")
|
| 271 |
|
| 272 |
+
# Load model - exactly like your working version
|
| 273 |
pipe = StableDiffusionPipeline.from_pretrained(
|
| 274 |
model_id,
|
| 275 |
torch_dtype=torch.float32,
|
| 276 |
safety_checker=None,
|
| 277 |
requires_safety_checker=False,
|
| 278 |
+
cache_dir="./model_cache"
|
|
|
|
|
|
|
| 279 |
)
|
| 280 |
|
| 281 |
+
# Use the same scheduler as working version
|
| 282 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
| 283 |
|
| 284 |
+
# Move to CPU - like working version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
pipe = pipe.to("cpu")
|
| 286 |
|
| 287 |
+
# NO additional optimizations - exactly like working version
|
| 288 |
+
|
| 289 |
model_cache[model_name] = pipe
|
| 290 |
current_pipe = pipe
|
| 291 |
current_model_name = model_name
|
|
|
|
| 305 |
"runwayml/stable-diffusion-v1-5",
|
| 306 |
torch_dtype=torch.float32,
|
| 307 |
safety_checker=None,
|
| 308 |
+
requires_safety_checker=False
|
|
|
|
| 309 |
).to("cpu")
|
| 310 |
|
|
|
|
|
|
|
| 311 |
model_cache[model_name] = pipe
|
| 312 |
current_pipe = pipe
|
| 313 |
current_model_name = "sd-1.5"
|
|
|
|
| 413 |
print(f"❌ Failed to upload image to HF Dataset: {e}")
|
| 414 |
return None
|
| 415 |
|
| 416 |
+
# PROMPT ENGINEERING - FROM WORKING VERSION
|
| 417 |
def enhance_prompt_simple(scene_visual, style="childrens_book"):
|
| 418 |
+
"""Simple prompt enhancement - uses only the provided visual prompt with style"""
|
| 419 |
|
| 420 |
style_templates = {
|
| 421 |
"childrens_book": "children's book illustration, watercolor style, soft colors, whimsical, magical, storybook art, professional illustration",
|
|
|
|
| 436 |
return enhanced_prompt, negative_prompt
|
| 437 |
|
| 438 |
# =============================================
|
| 439 |
+
# IMAGE GENERATION - EXACTLY LIKE WORKING VERSION
|
| 440 |
# =============================================
|
| 441 |
def generate_image_simple(prompt, model_choice, style, scene_number, consistency_seed=None):
|
| 442 |
+
"""Generate image - exactly like working version"""
|
| 443 |
|
| 444 |
if current_pipe is None:
|
| 445 |
+
if model_loading:
|
| 446 |
+
raise Exception("Model is still loading. Please wait a few seconds and try again.")
|
| 447 |
+
else:
|
| 448 |
+
raise Exception(f"Model failed to load: {model_load_error}")
|
| 449 |
|
| 450 |
enhanced_prompt, negative_prompt = enhance_prompt_simple(prompt, style)
|
| 451 |
|
|
|
|
| 457 |
try:
|
| 458 |
pipe = current_pipe
|
| 459 |
|
| 460 |
+
# Use full quality settings like working version
|
| 461 |
+
image = pipe(
|
| 462 |
+
prompt=enhanced_prompt,
|
| 463 |
+
negative_prompt=negative_prompt,
|
| 464 |
+
num_inference_steps=35,
|
| 465 |
+
guidance_scale=7.5,
|
| 466 |
+
width=768,
|
| 467 |
+
height=1024,
|
| 468 |
+
generator=torch.Generator(device="cpu").manual_seed(scene_seed)
|
| 469 |
+
).images[0]
|
|
|
|
|
|
|
|
|
|
| 470 |
|
| 471 |
print(f"✅ Generated image for scene {scene_number}")
|
| 472 |
return image
|
|
|
|
| 475 |
print(f"❌ Generation failed: {str(e)}")
|
| 476 |
raise
|
| 477 |
|
| 478 |
+
# LOCAL FILE MANAGEMENT FUNCTIONS - FROM WORKING VERSION
|
| 479 |
def save_image_to_local(image, prompt, style="test"):
|
| 480 |
"""Save image to local persistent storage"""
|
| 481 |
try:
|
|
|
|
| 631 |
|
| 632 |
# BACKGROUND TASK
|
| 633 |
def generate_storybook_background(job_id: str):
|
| 634 |
+
"""Background task to generate storybook"""
|
| 635 |
try:
|
| 636 |
if HF_TOKEN:
|
| 637 |
ensure_dataset_exists()
|
|
|
|
| 760 |
"clear_memory": "POST /api/clear-memory",
|
| 761 |
"local_images": "GET /api/local-images"
|
| 762 |
},
|
| 763 |
+
"ui": "/ui"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
}
|
| 765 |
|
| 766 |
@app.get("/api/health")
|
|
|
|
| 939 |
print("📡 API endpoints:")
|
| 940 |
print(" - GET /ping")
|
| 941 |
print(" - GET /debug")
|
| 942 |
+
print(" - GET /")
|
| 943 |
print(" - GET /api/health")
|
| 944 |
print(" - POST /api/generate-storybook")
|
| 945 |
+
print(" - GET /api/job-status/{job_id}")
|
| 946 |
+
print(" - GET /api/project-images/{project_id}")
|
| 947 |
print(" - GET /api/memory-status")
|
| 948 |
print(" - POST /api/clear-memory")
|
| 949 |
print(" - GET /api/local-images")
|