Instructions to use tugrulv89/image21-evaluation-cache with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use tugrulv89/image21-evaluation-cache with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("tugrulv89/image21-evaluation-cache", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
Download handler/qwen21/handler.py from tugrulv89/image21-evaluation-cache: direct link, hf CLI and curl.
- Browser
- Download file 5.39 kB
-
https://huggingface.co/tugrulv89/image21-evaluation-cache/resolve/main/handler/qwen21/handler.py
- Command line
-
hf download hf://tugrulv89/image21-evaluation-cache/handler/qwen21/handler.py
-
curl -L -o handler.py https://huggingface.co/tugrulv89/image21-evaluation-cache/resolve/main/handler/qwen21/handler.py
5.39 kB
| """Isolated Qwen Image 2.1 research worker; the 2511 handler stays unchanged.""" | |
| import io | |
| import json | |
| import os | |
| import time | |
| import traceback | |
| import uuid | |
| from pathlib import Path | |
| os.environ['HF_HUB_CACHE'] = '/runpod-volume/huggingface-cache/hub' | |
| os.environ['HF_HUB_OFFLINE'] = '1' | |
| os.environ['TRANSFORMERS_OFFLINE'] = '1' | |
| import requests | |
| from PIL import Image | |
| _pipe = None | |
| _bucket = None | |
| def sampling(job): | |
| allowed = {'job_id', 'image_url', 'image_urls', 'prompt', 'negative_prompt', | |
| 'seed', 'size', 'num_inference_steps', 'true_cfg_scale'} | |
| if set(job) - allowed: | |
| raise ValueError('Unsupported job fields: ' + ', '.join(sorted(set(job) - allowed))) | |
| width, height = map(int, job.get('size', '1024*1536').split('*')) | |
| steps = int(job.get('num_inference_steps', 40)) | |
| cfg = float(job.get('true_cfg_scale', 1.0)) | |
| if not (64 <= width <= 4096 and 64 <= height <= 4096 and | |
| width % 16 == height % 16 == 0 and 1 <= steps <= 60 and 1 <= cfg <= 5): | |
| raise ValueError('Invalid resolution or sampling parameters') | |
| if not job.get('prompt', '').strip(): | |
| raise ValueError('An explicit editing prompt is required') | |
| return {'prompt': job['prompt'], 'negative_prompt': job.get('negative_prompt'), | |
| 'width': width, 'height': height, 'num_inference_steps': steps, | |
| 'true_cfg_scale': cfg, 'use_kv_cache': True, | |
| 'output_resolution': 1024} | |
| def cached_model_path(): | |
| folder = Path(os.environ['HF_HUB_CACHE']) / ( | |
| 'models--' + os.environ['QWEN21_MODEL_REPO'].replace('/', '--')) / ( | |
| 'snapshots/' + os.environ['QWEN21_MODEL_REVISION']) | |
| if not (folder / 'model_index.json').is_file(): | |
| raise FileNotFoundError('Pinned model is missing from RunPod model cache: ' + str(folder)) | |
| return str(folder) | |
| def pipeline(): | |
| global _pipe | |
| if _pipe is None: | |
| import torch | |
| from diffusers import QwenImage21Pipeline | |
| _pipe = QwenImage21Pipeline.from_pretrained( | |
| cached_model_path(), local_files_only=True, | |
| torch_dtype=torch.bfloat16) | |
| _pipe.enable_model_cpu_offload() | |
| return _pipe | |
| def upload(photo): | |
| global _bucket | |
| if _bucket is None: | |
| from google.cloud import storage | |
| from google.oauth2 import service_account | |
| raw = os.environ['JSON_PATH_KEY'] | |
| try: | |
| credentials = service_account.Credentials.from_service_account_info(json.loads(raw)) | |
| except json.JSONDecodeError: | |
| credentials = service_account.Credentials.from_service_account_file(raw) | |
| _bucket = storage.Client(credentials=credentials).bucket(os.environ.get('GCS_BUCKET_NAME', 'shoupapp')) | |
| buf = io.BytesIO() | |
| photo.save(buf, 'PNG') | |
| blob = _bucket.blob('research/qwen21/' + uuid.uuid4().hex + '.png') | |
| blob.upload_from_string(buf.getvalue(), content_type='image/png') | |
| return blob.public_url | |
| def handler(event): | |
| data = event['input'] | |
| if data.get('task') != 'edit' or not 1 <= len(data.get('jobs', [])) <= 4: | |
| raise ValueError('Research worker accepts one to four explicit edit jobs') | |
| results = [] | |
| for job in data['jobs']: | |
| try: | |
| args = sampling(job) | |
| urls = job.get('image_urls') or [job.get('image_url')] | |
| if not 1 <= len(urls) <= 10 or not all(urls): | |
| raise ValueError('One to ten source image URLs are required') | |
| images = [] | |
| for url in urls: | |
| response = requests.get(url, timeout=60) | |
| response.raise_for_status() | |
| images.append(Image.open(io.BytesIO(response.content)).convert('RGBA')) | |
| import torch | |
| pipe = pipeline() | |
| torch.cuda.synchronize() | |
| torch.cuda.reset_peak_memory_stats() | |
| started = time.monotonic() | |
| photo = pipe(image=images, generator=torch.Generator('cuda').manual_seed( | |
| int(job.get('seed', 20261040))), **args).images[0] | |
| torch.cuda.synchronize() | |
| inference_ms = round((time.monotonic() - started) * 1000) | |
| results.append({'job_id': job.get('job_id'), 'ok': True, | |
| 'output_url': upload(photo.convert('RGB')), | |
| 'native_alpha_range': photo.getchannel('A').getextrema() if photo.mode == 'RGBA' else None, | |
| 'inference_ms': inference_ms, | |
| 'denoising_steps': len(pipe.scheduler.timesteps), | |
| 'gpu': torch.cuda.get_device_name(), | |
| 'peak_reserved_gib': round(torch.cuda.max_memory_reserved() / 1024**3, 2), | |
| 'model_revision': os.environ['QWEN21_MODEL_REVISION']}) | |
| except Exception as exc: | |
| detail = traceback.format_exc() | |
| for key in ('HF_TOKEN', 'JSON_PATH_KEY'): | |
| if os.environ.get(key): | |
| detail = detail.replace(os.environ[key], '[REDACTED]') | |
| print('Research job failed:', type(exc).__name__, detail, flush=True) | |
| results.append({'job_id': job.get('job_id'), 'ok': False, | |
| 'error': type(exc).__name__ + ': edit failed; inspect private worker logs'}) | |
| return {'results': results} | |
| if __name__ == '__main__': | |
| import runpod | |
| runpod.serverless.start({'handler': handler}) | |