diff --git a/renderers/qwen3_vl.py b/renderers/qwen3_vl.py index dffdd48..80c9a3b 100644 --- a/renderers/qwen3_vl.py +++ b/renderers/qwen3_vl.py @@ -279,6 +279,19 @@ def _load_preprocessor_config_json(model_name_or_path: str) -> dict[str, Any]: candidates.append(Path(cached)) except Exception: pass + if not candidates: + # Cache miss for a hub-style id (hosted workers render models they + # never loaded locally): download the tiny file into the HF cache. + # Offline, this falls through to the error below. + try: + from huggingface_hub import hf_hub_download + + downloaded = hf_hub_download( + model_name_or_path, "preprocessor_config.json" + ) + candidates.append(Path(downloaded)) + except Exception: + pass for candidate in candidates: if candidate.is_file(): @@ -290,8 +303,9 @@ def _load_preprocessor_config_json(model_name_or_path: str) -> dict[str, Any]: raise RuntimeError( "Qwen raw image layout could not find preprocessor_config.json for " - f"{model_name_or_path!r}. Ensure the model is cached locally or set all " - "image_* layout fields explicitly in the renderer config." + f"{model_name_or_path!r}. Ensure the model is cached locally or " + "reachable on the Hugging Face Hub, or set all image_* layout fields " + "explicitly in the renderer config." ) diff --git a/tests/test_client.py b/tests/test_client.py index b5f70d6..42566cc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -587,6 +587,41 @@ def test_qwen3_vl_raw_layout_matches_real_processor(tmp_path, monkeypatch): ) +def test_qwen3_vl_preprocessor_config_hub_download_fallback(tmp_path, monkeypatch): + """Hub-style ids that miss the local HF cache fall back to + ``hf_hub_download``; download failure (offline) keeps the explicit-config + error.""" + import huggingface_hub + + from renderers.qwen3_vl import _load_preprocessor_config_json + + config = { + "patch_size": 16, + "temporal_patch_size": 2, + "merge_size": 2, + "size": {"shortest_edge": 65536, "longest_edge": 16777216}, + } + downloaded = tmp_path / "preprocessor_config.json" + downloaded.write_text(json.dumps(config)) + + def fake_download(repo_id, filename): + assert filename == "preprocessor_config.json" + return str(downloaded) + + monkeypatch.setattr(huggingface_hub, "try_to_load_from_cache", lambda *a, **k: None) + monkeypatch.setattr(huggingface_hub, "hf_hub_download", fake_download) + _load_preprocessor_config_json.cache_clear() + assert _load_preprocessor_config_json("org/uncached-model") == config + + def offline_download(repo_id, filename): + raise OSError("offline") + + monkeypatch.setattr(huggingface_hub, "hf_hub_download", offline_download) + _load_preprocessor_config_json.cache_clear() + with pytest.raises(RuntimeError, match="could not find preprocessor_config.json"): + _load_preprocessor_config_json("org/uncached-model-offline") + + # --------------------------------------------------------------------------- # Prompt overflow handling. # ---------------------------------------------------------------------------