From 6123b70b5c28215c6beb3cd227e72b6761fb0b5b Mon Sep 17 00:00:00 2001 From: dxrpl Date: Thu, 15 Oct 2026 01:15:08 +0200 Subject: [PATCH] fix(hwfit): keep non-LLM HF collection models out of the LLM catalog (#6731) Tracked HF collections also contain time-series forecasters, image encoders and image/video generators. They were imported as "general" LLMs and offered for vllm serve. Skip their pipeline tags; embedding, reranker and RL-tagged LLM repos stay. Co-Authored-By: Claude Opus 5.5 --- services/hwfit/hf_discovery.py | 16 ++++++++++ ...test_hwfit_hf_discovery_pipeline_filter.py | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 tests/test_hwfit_hf_discovery_pipeline_filter.py diff --git a/services/hwfit/hf_discovery.py b/services/hwfit/hf_discovery.py index 3bea44931..143245b95 100644 --- a/services/hwfit/hf_discovery.py +++ b/services/hwfit/hf_discovery.py @@ -202,10 +202,26 @@ def _infer_use_case(repo_id, pipeline_tag): return "general" +# HF collections mix in models no Cookbook LLM backend can serve (vision +# encoders, time-series forecasters, image/video generators). Image models +# have their own registry, so keep these out of the LLM catalog. +_NON_LLM_PIPELINE_TAGS = frozenset({ + "time-series-forecasting", + "zero-shot-image-classification", + "image-classification", + "image-feature-extraction", + "text-to-image", + "image-to-video", + "text-to-video", +}) + + def _entry_from_collection_item(collection, item, source): repo_id = item.get("id") or "" if item.get("type") != "model" or not repo_id: return None + if (item.get("pipeline_tag") or "").strip().lower() in _NON_LLM_PIPELINE_TAGS: + return None repo_prefix = source.get("repo_prefix") if repo_prefix and not repo_id.startswith(repo_prefix): return None diff --git a/tests/test_hwfit_hf_discovery_pipeline_filter.py b/tests/test_hwfit_hf_discovery_pipeline_filter.py new file mode 100644 index 000000000..f1b17468e --- /dev/null +++ b/tests/test_hwfit_hf_discovery_pipeline_filter.py @@ -0,0 +1,31 @@ +import pytest + +from services.hwfit.hf_discovery import _entry_from_collection_item + +_SOURCE = {"owner": "google", "provider": "Google"} +_COLLECTION = {"title": "collection"} + + +@pytest.mark.parametrize("repo_id,pipeline_tag", [ + ("google/timesfm-3.0-pytorch", "time-series-forecasting"), + ("google/siglip2-base-patch16-224", "zero-shot-image-classification"), + ("moonshotai/MoonViT-SO-400M", "image-feature-extraction"), + ("zai-org/CogView4-6B", "text-to-image"), + ("zai-org/CogVideoX-5b-I2V", "image-to-video"), + ("zai-org/CogVideoX1.5-5B", "text-to-video"), +]) +def test_non_llm_collection_models_are_skipped(repo_id, pipeline_tag): + item = {"id": repo_id, "type": "model", "pipeline_tag": pipeline_tag, "numParameters": 1_000_000_000} + assert _entry_from_collection_item(_COLLECTION, item, _SOURCE) is None + + +@pytest.mark.parametrize("repo_id,pipeline_tag", [ + ("google/gemma-4-12B-it", "image-text-to-text"), + ("Qwen/Qwen3.6-27B", "text-generation"), + ("Qwen/Qwen3-Embedding-4B", "feature-extraction"), + ("NousResearch/DeepHermes-Egregore-v2-RLAIF-8b-Atropos", "reinforcement-learning"), + ("Qwen/Qwen3-Next-80B-A3B-Instruct", ""), +]) +def test_servable_collection_models_are_kept(repo_id, pipeline_tag): + item = {"id": repo_id, "type": "model", "pipeline_tag": pipeline_tag, "numParameters": 4_000_000_000} + assert _entry_from_collection_item(_COLLECTION, item, _SOURCE) is not None