--- title: LoopVL emoji: šŸ” colorFrom: purple colorTo: yellow sdk: gradio sdk_version: 6.30.0 app_file: app.py short_description: Recurrent VLM with a controllable compute budget python_version: "3.12" startup_duration_timeout: 30m --- # LoopVL: Recurrent Vision-Language Model **LoopVL** ([TierFlow/LoopVL](https://huggingface.co/TierFlow/LoopVL)) is a ~1.6B vision-language model that reuses its 16-layer transformer stack in nested loops instead of stacking 128 layers. The distinctive control this demo exposes is the **recurrent inference budget**: the H (high) and L (low) cycle counts decide how many times the stack is applied per forward pass — unrolled depth `16 Ɨ H Ɨ (L+1)` effective layers. The schedule can be changed at runtime without reloading weights. Ask any question about an image, pick a schedule, and compare answers across compute budgets. - **Model**: [TierFlow/LoopVL](https://huggingface.co/TierFlow/LoopVL) (BF16 vision/language cores + FP32 projector, visual gate and anchor) - **Code**: [Tier-Flow/LoopVL](https://github.com/Tier-Flow/LoopVL) — the inference runtime (`hrm_penguin`, `penguin_encoder`, `modeling_loopvl.py`, `metadata.py`) is vendored here from that Apache-2.0 repository; the Penguin vision encoder keeps its Tencent `LICENSE.txt` alongside. - **Examples**: images from [linoyts/repo-to-space-example-inputs](https://huggingface.co/datasets/linoyts/repo-to-space-example-inputs) (free to use, CC0-style). ## How it works 1. The image is resized under a dynamic-resolution policy (256–2048 visual tokens) and patched by the PenguinVL vision encoder. 2. Visual tokens are injected into the text sequence; the HRM-Text recurrent LM loops its stack `H Ɨ (L+1)` times per forward pass, re-injecting an anchored version of the visual tokens after each high cycle, gated by a learned saturation-aware gate. 3. Decoding is greedy, recomputing the full sequence per token (no KV cache). ## Reproducing the reference The `answer` endpoint follows the authors' `runtime/infer.py` one-to-one: same prompt template (`condition\nImage:\n` + placeholders + `\nInstruction:\n…`), same greedy decode, same `decode(skip_special_tokens=True).strip()` postprocess.