| import io |
| import os |
| import modal |
|
|
| |
| |
| |
| |
| cache_volume = modal.Volume.from_name("flux-inductor-cache", create_if_missing=True) |
| CACHE_DIR = "/root/.cache/torch/inductor" |
|
|
| |
| image = ( |
| modal.Image.debian_slim(python_version="3.12") |
| .pip_install( |
| "diffusers", |
| "transformers", |
| "accelerate", |
| "pillow", |
| "torch", |
| "triton" |
| ) |
| |
| .env({"TORCHINDUCTOR_CACHE_DIR": CACHE_DIR}) |
| ) |
|
|
| app = modal.App("flux-klein-voxel-backend", image=image) |
|
|
| |
| |
| |
| @app.function() |
| def demo_stream_frame(img_bytes: bytes) -> bytes: |
| """Fallback route structurally aligned to match the frontend signature.""" |
| from PIL import Image, ImageDraw |
| |
| input_image = Image.open(io.BytesIO(img_bytes)).convert("RGB") |
| draw = ImageDraw.Draw(input_image) |
| draw.text((20, 20), "🛠️ WEBRTC PASSTHROUGH DEMO ACTIVE", fill=(0, 255, 0)) |
| |
| output_buffer = io.BytesIO() |
| input_image.save(output_buffer, format="JPEG", quality=85) |
| return output_buffer.getvalue() |
|
|
|
|
| |
| |
| |
| @app.cls( |
| gpu="A10G", |
| secrets=[modal.Secret.from_name("huggingface-secret")], |
| max_containers=5, |
| |
| volumes={CACHE_DIR: cache_volume} |
| ) |
| class VoxelModel: |
| |
| @modal.enter() |
| def load_pipeline(self): |
| import torch |
| from PIL import Image |
| from diffusers import AutoPipelineForImage2Image |
| |
| model_id = "AnimeOverlord/flux2-klein-4b-mc" |
| |
| self.pipe = AutoPipelineForImage2Image.from_pretrained( |
| model_id, |
| torch_dtype=torch.bfloat16, |
| token=os.environ["HF_TOKEN"] |
| ) |
| self.pipe.to("cuda") |
| self.pipe.enable_attention_slicing() |
|
|
| |
| |
| |
| print("Initializing torch.compile optimization loops...") |
| |
| |
| self.pipe.vae.to(memory_format=torch.channels_last) |
| |
| |
| self.pipe.transformer = torch.compile( |
| self.pipe.transformer, |
| mode="reduce-overhead", |
| fullgraph=False |
| ) |
|
|
| |
| |
| |
| print("Running warmup to build/load inductor cache...") |
| dummy_image = Image.new("RGB", (512, 512), (0, 0, 0)) |
| with torch.inference_mode(): |
| self.pipe( |
| prompt="warmup pass", |
| image=dummy_image, |
| strength=0.5, |
| num_inference_steps=2, |
| guidance_scale=1.0, |
| ) |
| print("Warmup complete. Ready for real-time requests!") |
|
|
| @modal.method() |
| def process_frame(self, img_bytes: bytes) -> bytes: |
| from PIL import Image |
| import torch |
| |
| |
| prompt = "isometric 3d minecraft block voxel style, high resolution, volumetric lighting" |
| strength = 0.45 |
| |
| input_image = Image.open(io.BytesIO(img_bytes)).convert("RGB") |
| input_image = input_image.resize((512, 512)) |
| |
| with torch.inference_mode(): |
| output_image = self.pipe( |
| prompt=prompt, |
| image=input_image, |
| strength=strength, |
| num_inference_steps=4, |
| guidance_scale=3.5, |
| ).images[0] |
| |
| output_buffer = io.BytesIO() |
| output_image.save(output_buffer, format="JPEG", quality=85) |
| return output_buffer.getvalue() |