AI SeedbankHelp preserve open and free AI for humanity's future

← All models

openbmb_MiniCPM-V-4_5

openbmb · View on Hugging Face ↗

Get this model

Download TorrentMagnet Link

Seeders: · Leechers:

Model card

The complete upstream card, rendered from this payload's README.md — the same hash-verified bytes the torrent distributes. Images and off-site links are removed; the original card on Hugging Face carries them.


pipeline_tag: image-text-to-text datasets:

  • openbmb/RLAIF-V-Dataset library_name: transformers language:
  • multilingual tags:
  • minicpm-v
  • vision
  • ocr
  • multi-image
  • video
  • custom_code license: apache-2.0

A GPT-4o Level MLLM for Single Image, Multi Image and High-FPS Video Understanding on Your Phone

GitHub | MiniCPM Wiki(Chinese) | CookBook | Technical Report | Demo

MiniCPM-V 4.5

MiniCPM-V 4.5 is the latest and most capable model in the MiniCPM-V series. The model is built on Qwen3-8B and SigLIP2-400M with a total of 8B parameters. It exhibits a significant performance improvement over previous MiniCPM-V and MiniCPM-o models, and introduces new useful features. Notable features of MiniCPM-V 4.5 include:

  • 🔥 State-of-the-art Vision-Language Capability. MiniCPM-V 4.5 achieves an average score of 77.0 on OpenCompass, a comprehensive evaluation of 8 popular benchmarks. With only 8B parameters, it surpasses widely used proprietary models like GPT-4o-latest, Gemini-2.0 Pro, and strong open-source models like Qwen2.5-VL 72B for vision-language capabilities, making it the most performant MLLM under 30B parameters.

  • 🎬 Efficient High-FPS and Long Video Understanding. Powered by a new unified 3D-Resampler over images and videos, MiniCPM-V 4.5 can now achieve 96x compression rate for video tokens, where 6 448x448 video frames can be jointly compressed into 64 video tokens (normally 1,536 tokens for most MLLMs). This means that the model can perceive significantly more video frames without increasing the LLM inference cost. This brings state-of-the-art high-FPS (up to 10FPS) video understanding and long video understanding capabilities on Video-MME, LVBench, MLVU, MotionBench, FavorBench, etc., efficiently.

  • ⚙️ Controllable Hybrid Fast/Deep Thinking. MiniCPM-V 4.5 supports both fast thinking for efficient frequent usage with competitive performance, and deep thinking for more complex problem solving. To cover efficiency and performance trade-offs in different user scenarios, this fast/deep thinking mode can be switched in a highly controlled fashion.

  • 💪 Strong OCR, Document Parsing and Others. Based on LLaVA-UHD architecture, MiniCPM-V 4.5 can process high-resolution images with any aspect ratio and up to 1.8 million pixels (e.g., 1344x1344), using 4x less visual tokens than most MLLMs. The model achieves leading performance on OCRBench, surpassing proprietary models such as GPT-4o-latest and Gemini 2.5. It also achieves state-of-the-art performance for PDF document parsing capability on OmniDocBench among general MLLMs. Based on the latest RLAIF-V and VisCPM techniques, it features trustworthy behaviors, outperforming GPT-4o-latest on MMHal-Bench, and supports multilingual capabilities in more than 30 languages.

  • 💫 Easy Usage. MiniCPM-V 4.5 can be easily used in various ways: (1) llama.cpp and ollama support for efficient CPU inference on local devices, (2) int4, GGUF and AWQ format quantized models in 16 sizes, (3) SGLang and vLLM support for high-throughput and memory-efficient inference, (4) fine-tuning on new domains and tasks with Transformers and LLaMA-Factory, (5) quick local WebUI demo, (6) optimized local iOS app on iPhone and iPad, and (7) online web demo on server. See our Cookbook for full usages!

Key Techniques

  • Architechture: Unified 3D-Resampler for High-density Video Compression. MiniCPM-V 4.5 introduces a 3D-Resampler that overcomes the performance-efficiency trade-off in video understanding. By grouping and jointly compressing up to 6 consecutive video frames into just 64 tokens (the same token count used for a single image in MiniCPM-V series), MiniCPM-V 4.5 achieves a 96× compression rate for video tokens. This allows the model to process more video frames without additional LLM computational cost, enabling high-FPS video and long video understanding. The architecture supports unified encoding for images, multi-image inputs, and videos, ensuring seamless capability and knowledge transfer.

  • Pre-training: Unified Learning for OCR and Knowledge from Documents. Existing MLLMs learn OCR capability and knowledge from documents in isolated training approaches. We observe that the essential difference between these two training approaches is the visibility of the text in images. By dynamically corrupting text regions in documents with varying noise levels and asking the model to reconstruct the text, the model learns to adaptively and properly switch between accurate text recognition (when text is visible) and multimodal context-based knowledge reasoning (when text is heavily obscured). This eliminates reliance on error-prone document parsers in knowledge learning from documents, and prevents hallucinations from over-augmented OCR data, resulting in top-tier OCR and multimodal knowledge performance with minimal engineering overhead.

  • Post-training: Hybrid Fast/Deep Thinking with Multimodal RL. MiniCPM-V 4.5 offers a balanced reasoning experience through two switchable modes: fast thinking for efficient daily use and deep thinking for complex tasks. Using a new hybrid reinforcement learning method, the model jointly optimizes both modes, significantly enhancing fast-mode performance without compromising deep-mode capability. Incorporated with RLPR and RLAIF-V, it generalizes robust reasoning skills from broad multimodal data while effectively reducing hallucinations.

Evaluation

Inference Efficiency

OpenCompass

Model Size Avg Score ↑ Total Inference Time ↓
GLM-4.1V-9B-Thinking 10.3B 76.6 17.5h
MiMo-VL-7B-RL 8.3B 76.4 11h
MiniCPM-V 4.5 8.7B 77.0 7.5h

Video-MME

Model Size Avg Score ↑ Total Inference Time ↓ GPU Mem ↓
Qwen2.5-VL-7B-Instruct 8.3B 71.6 3h 60G
GLM-4.1V-9B-Thinking 10.3B 73.6 2.63h 32G
MiniCPM-V 4.5 8.7B 73.5 0.26h 28G

Both Video-MME and OpenCompass were evaluated using 8×A100 GPUs for inference. The reported inference time of Video-MME includes full model-side computation, and excludes the external cost of video frame extraction (dependent on specific frame extraction tools) for fair comparison.

Examples

We deploy MiniCPM-V 4.5 on iPad M4 with iOS demo. The demo video is the raw screen recording without editing.

Framework Support Matrix

Category Framework Cookbook Link Upstream PR Supported since(branch) Supported since(release)
Edge(On-device) Llama.cpp Llama.cpp Doc #15575(2025-08-26) master(2025-08-26) b6282
Ollama Ollama Doc #12078(2025-08-26) Merging Waiting for official release
Serving(Cloud) vLLM vLLM Doc #23586(2025-08-26) main(2025-08-27) v0.10.2
SGLang SGLang Doc #9610(2025-08-26) Merging Waiting for official release
Finetuning LLaMA-Factory LLaMA-Factory Doc #9022(2025-08-26) main(2025-08-26) Waiting for official release
Quantization GGUF GGUF Doc
BNB BNB Doc
AWQ AWQ Doc
Demos Gradio Demo Gradio Demo Doc

Note: If you'd like us to prioritize support for another open-source framework, please let us know via this short form.

Usage

If you wish to enable thinking mode, provide the argument enable_thinking=True to the chat function.

Chat with Image

import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer

torch.manual_seed(100)

model = AutoModel.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True, # or openbmb/MiniCPM-o-2_6
    attn_implementation='sdpa', torch_dtype=torch.bfloat16) # sdpa or flash_attention_2, no eager
model = model.eval().cuda()
tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True) # or openbmb/MiniCPM-o-2_6

image = Image.open('./assets/minicpmo2_6/show_demo.jpg').convert('RGB')

enable_thinking=False # If `enable_thinking=True`, the thinking mode is enabled.
stream=True # If `stream=True`, the answer is string

# First round chat 
question = "What is the landform in the picture?"
msgs = [{'role': 'user', 'content': [image, question]}]

answer = model.chat(
    msgs=msgs,
    tokenizer=tokenizer,
    enable_thinking=enable_thinking,
    stream=True
)

generated_text = ""
for new_text in answer:
    generated_text += new_text
    print(new_text, flush=True, end='')

# Second round chat, pass history context of multi-turn conversation
msgs.append({"role": "assistant", "content": [generated_text]})
msgs.append({"role": "user", "content": ["What should I pay attention to when traveling here?"]})

answer = model.chat(
    msgs=msgs,
    tokenizer=tokenizer,
    stream=True
)

generated_text = ""
for new_text in answer:
    generated_text += new_text
    print(new_text, flush=True, end='')

You will get the following output:

# round1
The landform in the picture is karst topography. Karst landscapes are characterized by distinctive, jagged limestone hills or mountains with steep, irregular peaks and deep valleys—exactly what you see here These unique formations result from the dissolution of soluble rocks like limestone over millions of years through water erosion.

This scene closely resembles the famous karst landscape of Guilin and Yangshuo in China’s Guangxi Province. The area features dramatic, pointed limestone peaks rising dramatically above serene rivers and lush green forests, creating a breathtaking and iconic natural beauty that attracts millions of visitors each year for its picturesque views.

# round2
When traveling to a karst landscape like this, here are some important tips:

1. Wear comfortable shoes: The terrain can be uneven and hilly.
2. Bring water and snacks for energy during hikes or boat rides.
3. Protect yourself from the sun with sunscreen, hats, and sunglasses—especially since you’ll likely spend time outdoors exploring scenic spots.
4. Respect local customs and nature regulations by not littering or disturbing wildlife.

By following these guidelines, you'll have a safe and enjoyable trip while appreciating the stunning natural beauty of places such as Guilin’s karst mountains.

Chat with Video

## The 3d-resampler compresses multiple frames into 64 tokens by introducing temporal_ids. 
# To achieve this, you need to organize your video data into two corresponding sequences: 
#   frames: List[Image]
#   temporal_ids: List[List[Int]].

import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
from decord import VideoReader, cpu    # pip install decord
from scipy.spatial import cKDTree
import numpy as np
import math

model = AutoModel.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True,  # or openbmb/MiniCPM-o-2_6
    attn_implementation='sdpa', torch_dtype=torch.bfloat16) # sdpa or flash_attention_2, no eager
model = model.eval().cuda()
tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True)  # or openbmb/MiniCPM-o-2_6

MAX_NUM_FRAMES=180 # Indicates the maximum number of frames received after the videos are packed. The actual maximum number of valid frames is MAX_NUM_FRAMES * MAX_NUM_PACKING.
MAX_NUM_PACKING=3  # indicates the maximum packing number of video frames. valid range: 1-6
TIME_SCALE = 0.1 

def map_to_nearest_scale(values, scale):
    tree = cKDTree(np.asarray(scale)[:, None])
    _, indices = tree.query(np.asarray(values)[:, None])
    return np.asarray(scale)[indices]


def group_array(arr, size):
    return [arr[i:i+size] for i in range(0, len(arr), size)]

def encode_video(video_path, choose_fps=3, force_packing=None):
    def uniform_sample(l, n):
        gap = len(l) / n
        idxs = [int(i * gap + gap / 2) for i in range(n)]
        return [l[i] for i in idxs]
    vr = VideoReader(video_path, ctx=cpu(0))
    fps = vr.get_avg_fps()
    video_duration = len(vr) / fps
        
    if choose_fps * int(video_duration) <= MAX_NUM_FRAMES:
        packing_nums = 1
        choose_frames = round(min(choose_fps, round(fps)) * min(MAX_NUM_FRAMES, video_duration))
        
    else:
        packing_nums = math.ceil(video_duration * choose_fps / MAX_NUM_FRAMES)
        if packing_nums <= MAX_NUM_PACKING:
            choose_frames = round(video_duration * choose_fps)
        else:
            choose_frames = round(MAX_NUM_FRAMES * MAX_NUM_PACKING)
            packing_nums = MAX_NUM_PACKING

    frame_idx = [i for i in range(0, len(vr))]      
    frame_idx =  np.array(uniform_sample(frame_idx, choose_frames))

    if force_packing:
        packing_nums = min(force_packing, MAX_NUM_PACKING)
    
    print(video_path, ' duration:', video_duration)
    print(f'get video frames={len(frame_idx)}, packing_nums={packing_nums}')
    
    frames = vr.get_batch(frame_idx).asnumpy()

    frame_idx_ts = frame_idx / fps
    scale = np.arange(0, video_duration, TIME_SCALE)

    frame_ts_id = map_to_nearest_scale(frame_idx_ts, scale) / TIME_SCALE
    frame_ts_id = frame_ts_id.astype(np.int32)

    assert len(frames) == len(frame_ts_id)

    frames = [Image.fromarray(v.astype('uint8')).convert('RGB') for v in frames]
    frame_ts_id_group = group_array(frame_ts_id, packing_nums)
    
    return frames, frame_ts_id_group


video_path="video_test.mp4"
fps = 5 # fps for video
force_packing = None # You can set force_packing to ensure that 3D packing is forcibly enabled; otherwise, encode_video will dynamically set the packing quantity based on the duration.
frames, frame_ts_id_group = encode_video(video_path, fps, force_packing=force_packing)

question = "Describe the video"
msgs = [
    {'role': 'user', 'content': frames + [question]}, 
]


answer = model.chat(
    msgs=msgs,
    tokenizer=tokenizer,
    use_image_id=False,
    max_slice_nums=1,
    temporal_ids=frame_ts_id_group
)
print(answer)

Chat with multiple images

Click to show Python code running MiniCPM-V 4.5 with multiple images input.

import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer

model = AutoModel.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True,
    attn_implementation='sdpa', torch_dtype=torch.bfloat16) # sdpa or flash_attention_2
model = model.eval().cuda()
tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True)

image1 = Image.open('image1.jpg').convert('RGB')
image2 = Image.open('image2.jpg').convert('RGB')
question = 'Compare image 1 and image 2, tell me about the differences between image 1 and image 2.'

msgs = [{'role': 'user', 'content': [image1, image2, question]}]

answer = model.chat(
    msgs=msgs,
    tokenizer=tokenizer
)
print(answer)

In-context few-shot learning

Click to view Python code running MiniCPM-V 4.5 with few-shot input.

import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer

model = AutoModel.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True,
    attn_implementation='sdpa', torch_dtype=torch.bfloat16)
model = model.eval().cuda()
tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-V-4_5', trust_remote_code=True)

question = "production date" 
image1 = Image.open('example1.jpg').convert('RGB')
answer1 = "2023.08.04"
image2 = Image.open('example2.jpg').convert('RGB')
answer2 = "2007.04.24"
image_test = Image.open('test.jpg').convert('RGB')

msgs = [
    {'role': 'user', 'content': [image1, question]}, {'role': 'assistant', 'content': [answer1]},
    {'role': 'user', 'content': [image2, question]}, {'role': 'assistant', 'content': [answer2]},
    {'role': 'user', 'content': [image_test, question]}
]

answer = model.chat(
    msgs=msgs,
    tokenizer=tokenizer
)
print(answer)

License

Model License

  • The MiniCPM-o/V model weights and code are open-sourced under the Apache-2.0 license.
  • To help us better understand and support our users, we would deeply appreciate it if you could consider optionally filling out a brief registration "questionnaire".

Statement

  • As an LMM, MiniCPM-V 4.5 generates contents by learning a large amount of multimodal corpora, but it cannot comprehend, express personal opinions or make value judgement. Anything generated by MiniCPM-V 4.5 does not represent the views and positions of the model developers
  • We will not be liable for any problems arising from the use of the MinCPM-V models, including but not limited to data security issues, risk of public opinion, or any risks and problems arising from the misdirection, misuse, dissemination or misuse of the model.

Key Techniques and Other Multimodal Projects

👏 Welcome to explore key techniques of MiniCPM-V 4.5 and other multimodal projects of our team:

VisCPM | RLPR | RLHF-V | LLaVA-UHD | RLAIF-V

Citation

If you find our work helpful, please consider citing our papers 📝 and liking this project ❤️!

@misc{yu2025minicpmv45cookingefficient,
      title={MiniCPM-V 4.5: Cooking Efficient MLLMs via Architecture, Data, and Training Recipe}, 
      author={Tianyu Yu and Zefan Wang and Chongyi Wang and Fuwei Huang and Wenshuo Ma and Zhihui He and Tianchi Cai and Weize Chen and Yuxiang Huang and Yuanqian Zhao and Bokai Xu and Junbo Cui and Yingjing Xu and Liqing Ruan and Luoyuan Zhang and Hanyu Liu and Jingkun Tang and Hongyuan Liu and Qining Guo and Wenhao Hu and Bingxiang He and Jie Zhou and Jie Cai and Ji Qi and Zonghao Guo and Chi Chen and Guoyang Zeng and Yuxuan Li and Ganqu Cui and Ning Ding and Xu Han and Yuan Yao and Zhiyuan Liu and Maosong Sun},
      year={2025},
      eprint={2509.18154},
      archivePrefix={arXiv},
      primaryClass={cs.LG},
      url={https://arxiv.org/abs/2509.18154}, 
}

@article{yao2024minicpm,
  title={MiniCPM-V: A GPT-4V Level MLLM on Your Phone},
  author={Yao, Yuan and Yu, Tianyu and Zhang, Ao and Wang, Chongyi and Cui, Junbo and Zhu, Hongji and Cai, Tianchi and Li, Haoyu and Zhao, Weilin and He, Zhihui and others},
  journal={Nat Commun 16, 5509 (2025)},
  year={2025}
}

Magnet link

Opens the swarm directly in your torrent client — no file download needed. Copy-paste works too:

magnet:?xt=urn:btih:186d3c485a2254b891ffead8fdcb2bc93c49af5f&dn=openbmb_MiniCPM-V-4_5

Open magnet in torrent client · infohash 186d3c485a2254b891ffead8fdcb2bc93c49af5f

Files & hashes

PathSizesha1sha256
README.md24.9 KB (25,542 B)79ef5eb1e8e9cbac45634eb2f3a0680705bbded00919332bbca6c5d64f4bee3fe1f4c30a36245a430db2056a00d7b06444b34c4c
added_tokens.json2.8 KB (2,862 B)4238f03344983475055a3487e654be7f5bbd5e151f41288392d77dd5dd9f235bdef78a3152d41651ff56b15ebdec476e3aa1cb7f
config.json1.4 KB (1,461 B)126378bbec97e713ebbb7d2bb2b389543107fff11d6f6eb441da34dcb43db72cf3960254c7045cc2c595a3c836ae71136655ccc8
configuration_minicpm.py3.2 KB (3,288 B)5bfd3f4d7c2a00ded7f8c7d40e9433ea0dbd6370bfdb65b291246e0d709831df2e10aaba96b94d2369f670f3ad831711d2f97e45
generation_config.json268 B (268 B)233aece9d4f5ae3a524de27c8bab1bf74d25c1dc4009ca3d9e615561897c0f4963bd01c26bccff50eda4b00fcf2c13cf3fcc86db
image_processing_minicpmv.py20.3 KB (20,757 B)7ae14cf95bda781e178f9543a1635b02e360593952888748938f40fec0827d187383b4a8fa857f4cffad5a0939e2e449051950d7
merges.txt1.6 MB (1,671,853 B)31349551d90c7606f325fe0f11bbb8bd5fa0d7c78831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5
model-00001-of-00004.safetensors4.92 GB (5,286,612,176 B)c0cbffb30739dd2f9f09978e0e827f6852133d8522643c1760b755c3c83b8631cd767fd667cb8aaababee5e4b3113210cd6177f0
model-00002-of-00004.safetensors4.94 GB (5,301,855,088 B)313e76cc425954344ca8145fb46bdac43302863f8be1ef2c54e913ef8824520cfa9b4e604c2afcf089907992b340048bf502896a
model-00003-of-00004.safetensors4.23 GB (4,546,851,120 B)85892c899a8ea8a99dc83ac296272e615c0058a59a2bd9a7b254f0671bccb84953876111fa64b889507dc12e22e98a1143b64fd7
model-00004-of-00004.safetensors2.10 GB (2,256,571,800 B)a7edda61d9a2d0b20837b4393352a3c862227c2cf425aa9a351f9e9f600d1235e9a583ff6931a5225e7b2b02a159248b32bd522e
model.safetensors.index.json70.5 KB (72,172 B)6091053aca66160ee87b9433b444d6cc2fc6332b6336714ff43484b398f021536c5eb39b11b68c439420f570d618e8b8943a6749
modeling_minicpmv.py18.1 KB (18,583 B)d10cfc05681c8e19f33292c49f8a317658d1aed5da7e53e9fd1ee931f8888ea5d78a53a903edb97a5634f0961d3347571f411cc6
modeling_navit_siglip.py40.9 KB (41,835 B)6fe732cf0d5884613003c00bccea0c5ae64a1905c71ca316fe7405325b7097364f98f71c317fff8c9f5c8820f30d3362b1ed5bfa
preprocessor_config.json714 B (714 B)7111b617df9ae1127d9383c0983b996af98ff9575c0dbc92920ded2304e2ed6ad4251da56029aa1f3f69427117ac6df9bc68496e
processing_minicpmv.py10.8 KB (11,026 B)cefc24566249cd47da7b3d2ee3f936941455f4e4aeb292555aba3362108eb10827234c48b9fede526398cc15bdbec798ba2c7fdd
resampler.py11.5 KB (11,732 B)d7d1756294deb0f5a9e083a086392c786eda6f5a9258b6e6e0b66fd5d3ce7127372fd82eff3659481602658045c253ae18e8d5be
special_tokens_map.json11.8 KB (12,103 B)b0204ae49160c057726f8e0eb1eec485129bc87e742d22f66cc33f367b17d45d8dcb5ffde7d777fe94a9b05ee858dc43018c2016
tokenization_minicpmv_fast.py1.6 KB (1,647 B)c8411fa70c616474142442f883fc88b40363e0f2770e4a934f2e74e0e88e85126bceea93e345d84b411d21e99b38c5cfd095f736
tokenizer.json10.9 MB (11,437,868 B)c4e54e0c790024e537218a00d5081bb84b2e1cddc5a94a2c3913b8aa2175fffb5fd6cf4301958f323d06475bfd91037c13bdd74b
tokenizer_config.json25.2 KB (25,786 B)aaf1d40a5ce6edc302b3c5f8de9e6074b643ce598cbacbea273108831f02441518bbbc73627c15e34ddaa3fec6369e7bd07b720c
vocab.json2.6 MB (2,776,833 B)4783fe10ac3adce15ac8f358ef5462739852c569ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910

Cite this release

Canonical URL
https://aiseedbank.org/models/openbmb_MiniCPM-V-4_5/
Slug
openbmb_MiniCPM-V-4_5
Infohash
186d3c485a2254b891ffead8fdcb2bc93c49af5f
License
apache-2.0
Signing key fingerprint
85a3b32c3712427b

Every file carries a locally computed sha256 — verify a download against the signed sums: openbmb_MiniCPM-V-4_5.SHA256SUMS (+ minisign signature).

Provenance

Upstream repositoryopenbmb/MiniCPM-V-4_5
Revision (pinned)daef484c35ec93210ec93c5e901f8f3e9b78ee34
Fetched at2026-09-04T04:47:25Z
License at fetchapache-2.0
Snapshot toolhuggingface · seedbank 0.1.0

Trackers

✓ verified · rehash-vs-hf-metadata at 2026-09-04T04:50:34Z

apache-2.016.21 GB (17,408,026,514 bytes)transformerssafetensorsminicpmvfeature-extractionminicpm-vvisionocrmulti-imagevideocustom_codeimage-text-to-textconversationalmultilingualeval-resultspaper: 2509.18154paper: 2403.11703