| # AGENT.md |
|
|
| This file provides guidance to AI coding agents when working with code in this repository. |
|
|
| ## Project Overview |
|
|
| This is a Gradio Space that implements "Next Scene" cinematic image generation using Qwen-Image-Edit-2509 with LoRA fine-tuning. The application generates visually progressive image sequences with natural cinematic transitions from frame to frame, optimized for fast 4-step inference. |
|
|
| **Key Model Components:** |
| - Base model: `Qwen/Qwen-Image-Edit-2509` (image editing diffusion model) |
| - Accelerated transformer: `linoyts/Qwen-Image-Edit-Rapid-AIO` (4-step optimized variant) |
| - LoRA adapter: `lovis93/next-scene-qwen-image-lora-2509` (cinematic progression fine-tune) |
| - Text encoder: `Qwen2.5-VL-72B-Instruct` (via Hugging Face InferenceClient for prompt enhancement) |
|
|
| ## Running the Application |
|
|
| **Start the Gradio interface:** |
| ```bash |
| python app.py |
| ``` |
|
|
| **Install dependencies:** |
| ```bash |
| pip install -r requirements.txt |
| ``` |
|
|
| The app requires GPU access. It uses the `@spaces.GPU` decorator for Hugging Face Spaces zero-GPU allocation. |
|
|
| ## Architecture |
|
|
| ### Pipeline Flow |
|
|
| 1. **Input Processing** (`app.py:infer`): |
| - Accepts input images via Gradio Gallery (filepath-based) |
| - Optional prompt rewriting using `Qwen2.5-VL-72B-Instruct` API |
| - Automatic "Next Scene" prompt generation from images |
|
|
| 2. **Image Generation** (`qwenimage/pipeline_qwenimage_edit_plus.py`): |
| - Custom pipeline extending `DiffusionPipeline` |
| - Encodes images using VAE at 1024x1024 for latents |
| - Encodes conditioning images at 384x384 for text encoder |
| - Packs latents into 2x2 patches (latent dims must be divisible by 2) |
| - Uses `FlowMatchEulerDiscreteScheduler` for denoising |
|
|
| 3. **Optimization** (`optimization.py`): |
| - Ahead-of-time (AOT) compilation using `torch.export` and `spaces.aoti_compile` |
| - Dynamic shapes for variable sequence lengths |
| - Custom inductor configs for performance (max_autotune, cudagraphs) |
| - FlashAttention 3 integration via `QwenDoubleStreamAttnProcessorFA3` |
| |
| 4. **Output Handling**: |
| - Saves outputs to `outputs/` directory with unique timestamps |
| - Maintains 20-image history gallery |
| - Optional video generation via `multimodalart/wan-2-2-first-last-frame` Space |
| |
| ### Custom QwenImage Components |
| |
| **Location:** `qwenimage/` package |
| |
| - `pipeline_qwenimage_edit_plus.py` - Main diffusion pipeline with LoRA support |
| - `transformer_qwenimage.py` - Custom transformer model with cache management |
| - `qwen_fa3_processor.py` - FlashAttention 3 attention processor |
|
|
| **Key architectural features:** |
| - Latent packing/unpacking for 2x2 patch processing |
| - Multi-image conditioning support |
| - True CFG (classifier-free guidance) with separate pos/neg paths |
| - Dual-stream attention with rotary embeddings |
| - Cache contexts for conditional/unconditional forward passes |
|
|
| ### Prompt Engineering |
|
|
| **Two-stage prompt system:** |
|
|
| 1. **Edit Instruction Rewriter** (`SYSTEM_PROMPT`): |
| - Normalizes user prompts into professional editing instructions |
| - Handles text replacement (requires quotes), object manipulation, style transfer |
| - Used when `rewrite_prompt=True` checkbox is enabled |
|
|
| 2. **Next Scene Generator** (`NEXT_SCENE_SYSTEM_PROMPT`): |
| - Automatically suggests cinematic camera movements |
| - Focus on visual progression (dolly, pan, zoom, framing changes) |
| - Auto-triggers when input images change |
|
|
| Both use `Qwen2.5-VL-72B-Instruct` via Hugging Face InferenceClient with Nebius provider. Requires `HF_TOKEN` environment variable. |
|
|
| ## Important Implementation Details |
|
|
| ### Image Dimension Handling |
|
|
| Images are automatically resized based on `calculate_dimensions()` function: |
| - VAE images: resized to maintain 1024×1024 area (1,048,576 pixels) |
| - Condition images: resized to maintain 384×384 area (147,456 pixels) |
| - Output dimensions must be divisible by 16 (vae_scale_factor × 2) |
| - Height/width default to `None` which auto-calculates from input aspect ratio |
|
|
| ### LoRA Integration |
|
|
| The pipeline fuses the "next-scene" LoRA adapter at initialization: |
| ```python |
| pipe.load_lora_weights("lovis93/next-scene-qwen-image-lora-2509", ...) |
| pipe.set_adapters(["next-scene"], adapter_weights=[1.]) |
| pipe.fuse_lora(adapter_names=["next-scene"], lora_scale=1.) |
| pipe.unload_lora_weights() |
| ``` |
|
|
| After fusion, the adapter weights are merged into the base model and cannot be unfused. |
|
|
| ### Video Generation Integration |
|
|
| The `turn_into_video()` function: |
| - Connects to external Gradio Space `multimodalart/wan-2-2-first-last-frame` |
| - Requires first input image and last output image |
| - Uses the original prompt (or "smooth cinematic transition" fallback) |
| - Returns video path for display |
|
|
| ### Gradio Gallery Format |
|
|
| Input/output galleries use `type="filepath"` (string paths) rather than PIL Image tuples. Helper functions handle format compatibility for legacy tuple support. |
|
|
| ## Environment Variables |
|
|
| - `HF_TOKEN` - Required for Qwen2.5-VL API access (prompt rewriting/generation) |
|
|
| ## File Outputs |
|
|
| Generated images are saved to `outputs/` directory with format: |
| ``` |
| output_{seed}_{index}_{timestamp_ms}.png |
| ``` |
|
|
| ## Local Development and API Testing |
|
|
| The `custom/` directory is fully gitignored and used for local development files. Specifically, it contains: |
|
|
| - **API client scripts** - For testing the Gradio Space remotely via API after deployment to Hugging Face |
| - **`API_GUIDE.txt`** - Auto-generated Gradio API documentation showing endpoint signatures and example usage |
| - **Local testing environments** - Virtual environments or test data that shouldn't be committed |
| |
| **API Integration Pattern:** |
| Once the Space is deployed to Hugging Face, you can interact with it programmatically using `gradio_client`: |
| |
| ```python |
| from gradio_client import Client, handle_file |
| |
| client = Client("Sneak-Moose/Qwen-Image-Edit-next-scene") |
| result = client.predict( |
| images=[], |
| prompt="Next Scene: Camera dollies forward...", |
| seed=42, |
| randomize_seed=False, |
| true_guidance_scale=1.0, |
| num_inference_steps=4, |
| height=1024, |
| width=1024, |
| rewrite_prompt=False, |
| api_name="/infer" |
| ) |
| ``` |
| |
| The `custom/API_GUIDE.txt` contains full documentation of all available endpoints including `/infer`, `/turn_into_video`, `/suggest_next_scene_prompt`, and utility functions. |
| |
| ## Development Notes |
| |
| - The model loads on startup and applies AOT compilation during first inference |
| - Compilation uses dynamic shapes to support variable text/image sequence lengths |
| - The transformer uses custom cache contexts ("cond"/"uncond") to optimize CFG passes |
| - True CFG applies norm-based rescaling: `comb_pred * (cond_norm / noise_norm)` |
| - FlashAttention 3 processor must be set before compilation |
| |