fix: stop grouping HF/ModelScope models by repository

A repository is not a model identity: collection repos on Hugging Face
and ModelScope host many unrelated models, which were wrongly shown as
versions of each other.

- Hugging Face models no longer auto-group (the Hub exposes no
  site-native model id)
- ModelScope models group by the site's native published-model id
  (MuseInfo modelVersion.modelId), extracted during enrichment and
  persisted on the sidecar as source_model_id/source_version_id;
  unenriched models stay standalone instead of collapsing a whole repo
  into one group
- TensorArt grouping unchanged (its URL id is already model-level)
- Frontend group-key derivation mirrors the new backend semantics
This commit is contained in:
Will Miao
2026-09-25 23:29:30 +08:00
parent 8a80f82d93
commit c48feeddb6
17 changed files with 357 additions and 78 deletions
+43 -6
View File
@@ -279,15 +279,39 @@ class TestHelpers:
assert get_source_platform({"source_platform": "tensorart"}) == "tensorart"
assert get_source_platform({}) == ""
def test_group_keys_match_legacy_hf_shape(self):
assert source_group_key({"hf_url": "https://huggingface.co/u/r"}) == "hf:u/r"
assert (
source_group_key({"source_url": "https://modelscope.cn/models/u/r"}) == "ms:u/r"
)
def test_group_keys_use_site_native_identity(self):
# Hugging Face has no site-native model identity: never grouped.
assert source_group_key({"hf_url": "https://huggingface.co/u/r"}) is None
# TensorArt's numeric id already identifies a single model.
assert (
source_group_key({"source_url": "https://tensor.art/models/123"}) == "ta:123"
)
def test_modelscope_groups_by_published_model_id(self):
# Without an enriched source_model_id the model stays standalone —
# never grouped by repo, which would collapse a collection repo.
assert (
source_group_key({"source_url": "https://modelscope.cn/models/u/r"}) is None
)
assert (
source_group_key(
{
"source_url": "https://modelscope.cn/models/u/r",
"source_model_id": "555",
}
)
== "ms:555"
)
assert (
source_group_key(
{
"source_url": "https://www.modelscope.ai/models/u/r",
"source_model_id": "678",
}
)
== "msai:678"
)
def test_group_key_is_none_without_source(self):
assert source_group_key({}) is None
assert source_group_key({"hf_url": "https://example.com/x"}) is None
@@ -457,7 +481,12 @@ def _modelscope_detail_payload() -> dict:
"versions": [
{
"stats": {"fileList": ["Krea-2-LORA_c1-st8000.safetensors"]},
"modelVersion": {"showName": "c1-st8000", "triggerWords": '[""]'},
"modelVersion": {
"showName": "c1-st8000",
"triggerWords": '[""]',
"id": 1001,
"modelId": 555,
},
"coverImages": [
{"url": "https://resources.modelscope.cn/cover-images/a.png"}
],
@@ -467,6 +496,8 @@ def _modelscope_detail_payload() -> dict:
"modelVersion": {
"showName": "c1-st1000",
"triggerWords": '["kreaface","kreamodel"]',
"id": 1002,
"modelId": 555,
},
"coverImages": [
{"url": "https://resources.modelscope.cn/cover-images/b.png"},
@@ -533,6 +564,9 @@ class TestFetchModelCardContext:
# The version label is taken from the file that was matched, not from
# whichever version happens to come first in the payload.
assert context.version_name == "c1-st1000"
# The site-native identity ids belong to the matched version too.
assert context.source_model_id == "555"
assert context.source_version_id == "1002"
@pytest.mark.asyncio
async def test_modelscope_version_label_is_empty_for_an_unknown_file(
@@ -550,6 +584,9 @@ class TestFetchModelCardContext:
)
assert context.version_name == ""
# No version matched, so there is no per-version identity either.
assert context.source_model_id == ""
assert context.source_version_id == ""
# The repository-wide fields are still published.
assert context.model_name == "Krea-2-LORA"