From 91f3040dcc1bb1228c2ae36c4163beb76fb1b58e Mon Sep 17 00:00:00 2001 From: hubert-marek Date: Tue, 9 Jun 2026 20:50:07 +0000 Subject: [PATCH 1/2] fix(qwen3-vl): fall back to hf_hub_download for mmraw preprocessor_config.json The raw-image (mmraw) layout path resolves the model's image geometry from preprocessor_config.json, but _load_preprocessor_config_json only checked local paths and the local HF cache (try_to_load_from_cache). Hosted env workers render models they never loaded locally, so hub-style ids always missed the cache and every image rollout failed with RuntimeError: Qwen raw image layout could not find preprocessor_config.json for 'Qwen/Qwen3.6-35B-A3B' ... even when the file is publicly available on the Hub. Add an hf_hub_download fallback on cache miss (a few hundred bytes, lands in the HF cache, then memoized by the lru_cache). Offline/no-network workers fall through to the existing RuntimeError, whose message now also mentions Hub reachability alongside the explicit image_* config escape hatch. Co-Authored-By: Claude Opus 4.8 (1M context) --- renderers/qwen3_vl.py | 19 +++++++++++++++++-- tests/test_client.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/renderers/qwen3_vl.py b/renderers/qwen3_vl.py index dffdd48..3fe272e 100644 --- a/renderers/qwen3_vl.py +++ b/renderers/qwen3_vl.py @@ -279,6 +279,20 @@ 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: fall back to downloading the file + # (a few hundred bytes; lands in the HF cache so this is one-time + # per process pool). Hosted env workers render models they never + # loaded locally, so the cache-only lookup above rarely hits there. + # Offline/no-network environments fall through to the error below, + # which names the explicit image_* config escape hatch. + 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 +304,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. # --------------------------------------------------------------------------- From f1469353afebaa9378c89e6b61a0ed11d686872e Mon Sep 17 00:00:00 2001 From: hubert-marek Date: Wed, 10 Jun 2026 05:00:45 +0000 Subject: [PATCH 2/2] style: tighten hub-download fallback comment, wrap to line length Co-Authored-By: Claude Fable 5 --- renderers/qwen3_vl.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/renderers/qwen3_vl.py b/renderers/qwen3_vl.py index 3fe272e..80c9a3b 100644 --- a/renderers/qwen3_vl.py +++ b/renderers/qwen3_vl.py @@ -280,16 +280,15 @@ def _load_preprocessor_config_json(model_name_or_path: str) -> dict[str, Any]: except Exception: pass if not candidates: - # Cache miss for a hub-style id: fall back to downloading the file - # (a few hundred bytes; lands in the HF cache so this is one-time - # per process pool). Hosted env workers render models they never - # loaded locally, so the cache-only lookup above rarely hits there. - # Offline/no-network environments fall through to the error below, - # which names the explicit image_* config escape hatch. + # 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") + downloaded = hf_hub_download( + model_name_or_path, "preprocessor_config.json" + ) candidates.append(Path(downloaded)) except Exception: pass