HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import pytest23from aiatlas.connectors.hub.huggingface import HuggingFaceConnector, artifact_kind, canonical_name, pipeline_modalities4from aiatlas.sdk.facts import Target5from tests.conftest import claims_of, entity_names, extract_from_fixture, fixture_path678@pytest.fixture9def connector() -> HuggingFaceConnector:10 return HuggingFaceConnector({"models_per_org": 20})111213async def test_discover_uses_registry_orgs(connector):14 targets = await connector.discover(None) # type: ignore[arg-type]15 urls = {t.url for t in targets}16 assert "https://huggingface.co/papers" in urls17 assert "https://huggingface.co/models?author=Qwen&sort=downloads&p=0" in urls18 assert "https://huggingface.co/models?author=bartowski&sort=downloads&p=0" in urls19 assert len(targets) >= 40 and not any("/api/" in u for u in urls)202122async def test_listing(connector):23 target = Target(url="https://huggingface.co/models?author=Qwen&sort=downloads&p=0", doc_type="listing", key="listing:Qwen:0", meta={"hf_org": "Qwen", "page": 0})24 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "listing-qwen.html"))25 models = [e for e in facts.entities if e.entity_type == "model"]26 artifacts = [e for e in facts.entities if e.entity_type == "artifact"]27 assert len(models) + len(artifacts) == 20 and len(artifacts) == 3 # models_per_org cap; Qwen's own FP8 repos are artifacts28 assert models[0].identifiers == {"hf_repo": "Qwen/Qwen3-0.6B"} and models[0].organization.name == "Qwen"29 assert models[0].family is not None and models[0].family.entity_type == "model_family" and models[0].family.name == "Qwen3"30 fp8 = next(a for a in artifacts if a.name == "Qwen/Qwen3.8-27B-FP8")31 assert fp8.artifact_kind == "quantization" and fp8.canonical is not None and fp8.canonical.name == "Qwen3.8-27B"32 assert fp8.canonical.identifiers == {"hf_repo": "Qwen/Qwen3.8-27B"} # the official repo appears later in the same listing → one ref33 assert sum(1 for e in models if e.name == "Qwen3.8-27B") == 134 c = claims_of(facts, "Qwen3-0.6B")35 assert c["metric.downloads"] == 20685071 and c["pipeline_tag"] == "text-generation" and c["access"] == "open" and c["weights_available"] is True36 assert c["modalities_input"] == ["text"] and c["modalities_output"] == ["text"] and "openness" not in c # openness needs the licence (model page)37 assert c["parameter_count"] == 75163238438 assert [t.url for t in facts.targets][:2] == ["https://huggingface.co/Qwen/Qwen3-0.6B", "https://huggingface.co/Qwen/Qwen3-VL-8B-Instruct"]39 assert all(t.doc_type == "model_page" for t in facts.targets) and len(facts.targets) == 2040 assert not any(r.predicate == "develops" and r.object.entity_type == "artifact" for r in facts.relations)414243async def test_model_page(connector):44 target = Target(url="https://huggingface.co/Qwen/Qwen3-8B", doc_type="model_page", key="model:Qwen/Qwen3-8B", meta={"hf_repo": "Qwen/Qwen3-8B"})45 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "model-qwen3-8b.html"))46 c = claims_of(facts, "Qwen3-8B")47 assert c["license"] == "Apache-2.0" and c["license_raw"] == "apache-2.0" and c["library_name"] == "transformers" and c["architecture"] == "Qwen3ForCausalLM"48 assert c["parameter_count"] == 8190735360 and c["release_date"] == "2025-04-27" and c["openness"] == "open-weights"49 assert c["access"] == "open" and c["weights_available"] is True and "gated" not in c50 assert c["metric.likes"] == 1366 and c["file_size_gb"] == 16.38 and c["base_model"] == ["Qwen/Qwen3-8B-Base"]51 rels = {(r.subject.name, r.predicate, r.object.name) for r in facts.relations}52 assert ("Qwen3-8B", "fine_tuned_from", "Qwen3-8B-Base") in rels and ("Qwen", "develops", "Qwen3-8B") in rels53 assert ("Qwen3-8B", "described_by", "arXiv:2505.09388") in rels54 assert [t.url for t in facts.targets] == ["https://huggingface.co/Qwen/Qwen3-8B/raw/main/README.md"]55 assert facts.document_entity is not None and facts.document_entity.identifiers == {"hf_repo": "Qwen/Qwen3-8B"}56 assert facts.document_entity.entity_type == "model" and facts.document_entity.family.name == "Qwen3" and facts.document_entity.identity_confidence == "high"575859async def test_model_page_gguf_and_gated(connector):60 target = Target(url="https://huggingface.co/bartowski/Qwen3.8-27B-GGUF", doc_type="model_page", key="x", meta={"hf_repo": "bartowski/Qwen3.8-27B-GGUF"})61 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "model-bartowski-gguf.html"))62 art = next(e for e in facts.entities if e.entity_type == "artifact")63 assert art.name == "bartowski/Qwen3.8-27B-GGUF" and art.artifact_kind == "quantization" and art.identifiers == {"hf_repo": "bartowski/Qwen3.8-27B-GGUF"}64 assert art.canonical is not None and art.canonical.entity_type == "model" and art.canonical.name == "Qwen3.8-27B"65 assert art.canonical.organization.name == "Qwen" and art.canonical.identity_confidence == "medium" and art.canonical.family.name == "Qwen3.8"66 c = claims_of(facts, "bartowski/Qwen3.8-27B-GGUF") # artifacts keep the full id as name67 assert c["quant_format"] == "gguf" and "Q4_K_M" in c["quantization"] and c["is_quantized"] is True and c["quantized_by"] == "bartowski"68 assert c["artifact_kind"] == "quantization" and c["parameter_count"] == 27_000_000_000 # the packaged size stays on the artifact…69 assert "parameter_count" not in claims_of(facts, "Qwen3.8-27B") # …never on the canonical model70 assert c["modalities_input"] == ["image", "text"] and c["modalities_output"] == ["text"] # image-text-to-text71 assert facts.entities[0].name == "bartowski" and facts.entities[0].identifiers["registry_org"] == "bartowski"72 rels = {(r.subject.name, r.predicate, r.object.name) for r in facts.relations}73 assert ("bartowski/Qwen3.8-27B-GGUF", "published_by", "bartowski") in rels and not any(p == "develops" for _s, p, _o in rels)7475 target = Target(url="https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct", doc_type="model_page", key="x", meta={"hf_repo": "meta-llama/Llama-3.1-8B-Instruct"})76 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "model-llama-gated.html"))77 c = claims_of(facts, "Llama-3.1-8B-Instruct")78 assert c["access"] == "gated" and c["gated_mode"] == "manual" and "restricted" not in str(c.get("status"))79 assert c["license"] == "Llama-3.1-Community" and c["license_raw"] == "llama3.1" and c["openness"] == "restricted-weights" and c["weights_available"] is True80 assert c["languages"] == ["en", "de", "fr", "it", "pt", "hi", "es", "th"]81 assert facts.targets == [] # gated card is not fetched (401 without auth)82 assert any(r.predicate == "fine_tuned_from" and r.attributes.get("base_model_relation") == "finetune" for r in facts.relations)83 llama = next(e for e in facts.entities if e.name == "Llama-3.1-8B-Instruct")84 assert llama.entity_type == "model" and llama.family.name == "Llama 3.1" and llama.family.organization.name == "Meta AI"858687async def test_model_card(connector):88 target = Target(url="https://huggingface.co/bartowski/Qwen3.8-27B-GGUF/raw/main/README.md", doc_type="model_card", key="x", meta={"hf_repo": "bartowski/Qwen3.8-27B-GGUF"})89 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "readme-bartowski-gguf.md"), content_type="text/markdown")90 assert claims_of(facts, "bartowski/Qwen3.8-27B-GGUF")["base_model"] == ["Qwen/Qwen3.8-27B"]91 assert ("bartowski/Qwen3.8-27B-GGUF", "quantized_from", "Qwen3.8-27B") in {(r.subject.name, r.predicate, r.object.name) for r in facts.relations}92 art = next(e for e in facts.entities if e.entity_type == "artifact")93 assert art.canonical is not None and art.canonical.identifiers == {"hf_repo": "Qwen/Qwen3.8-27B"} and art.identity_confidence == "high"94 target = Target(url="https://huggingface.co/Qwen/Qwen3-8B/raw/main/README.md", doc_type="model_card", key="x", meta={"hf_repo": "Qwen/Qwen3-8B"})95 facts = await extract_from_fixture(connector, target, fixture_path("huggingface", "readme-qwen3-8b.md"), content_type="text/markdown")96 assert claims_of(facts, "Qwen3-8B")["license"] == "Apache-2.0"97 assert not any(r.predicate in ("derived_from", "quantized_from") for r in facts.relations) # typed relation comes from the page9899100def test_identity_helpers():101 from aiatlas.connectors.hub.huggingface import _foreign_family102103 assert _foreign_family("NousResearch", "Meta-Llama-3.1-70B-Instruct") is True and _foreign_family("meta-llama", "Llama-3.1-70B-Instruct") is False104 assert _foreign_family("NousResearch", "Hermes-4-70B") is False and _foreign_family("Qwen", "Qwen3-8B") is False105 assert _foreign_family("mlx-community", "Kimi-K2.5") is True106 assert canonical_name("Qwen3.8-27B-GGUF") == "Qwen3.8-27B" and canonical_name("Llama-3.1-8B-Instruct-bnb-4bit") == "Llama-3.1-8B-Instruct"107 assert canonical_name("DeepSeek-V3-0324-UD-Q4_K_XL") == "DeepSeek-V3-0324" and canonical_name("Kimi-K2.5-bf16") == "Kimi-K2.5"108 assert artifact_kind("zai-org/GLM-5-FP8", bases=[], quant_format=None) == "quantization"109 assert artifact_kind("mlx-community/Kimi-K2.5", bases=[], quant_format=None) == "packaging"110 assert artifact_kind("unsloth/Qwen3-8B-bf16", bases=[], quant_format=None) == "conversion"111 assert artifact_kind("Qwen/Qwen3-8B", bases=[("Qwen/Qwen3-8B-Base", "finetune")], quant_format=None) is None112 assert artifact_kind("NousResearch/Hermes-4-70B", bases=[("meta-llama/Llama-3.1-70B", "finetune")], quant_format=None) is None # fine-tune = model113 assert pipeline_modalities("image-text-to-text") == (["image", "text"], ["text"]) and pipeline_modalities("automatic-speech-recognition") == (["audio"], ["text"])114 assert pipeline_modalities("text-to-image") == (["text"], ["image"]) and pipeline_modalities("unknown-tag") == ([], [])115116117def test_official_org_quantisations_are_artifacts(connector):118 """A quant/precision token makes the repo an artifact even under the developer's own org; canonical = the same-org model from the stripped119 name; None when the base cannot be named confidently."""120 from aiatlas.sdk.facts import Facts121122 cases = {"nvidia/Gemma-4-31B-IT-NVFP4": ("quantization", "Gemma-4-31B-IT", "Google"), # Gemma is Google's family: NVIDIA quantises it123 "tencent/HY-MT1.5-1.8B-FP8": ("quantization", "HY-MT1.5-1.8B", "Tencent"),124 "LiquidAI/LFM2.5-230M-GGUF": ("quantization", "LFM2.5-230M", "Liquid AI"), "black-forest-labs/FLUX.2-klein-4b-fp8": ("quantization", "FLUX.2-klein-4b", "Black Forest Labs"),125 "Qwen/Qwen3-8B-MLX-bf16": ("conversion", "Qwen3-8B", "Qwen")}126 for repo, (kind, base, org_name) in cases.items():127 facts = Facts()128 org = connector._org_ref(facts, repo.split("/")[0], None)129 ref = connector._model_ref(facts, repo, org)130 assert ref.entity_type == "artifact" and ref.artifact_kind == kind and ref.name == repo, repo131 assert ref.canonical is not None and ref.canonical.entity_type == "model" and ref.canonical.name == base, repo132 assert ref.canonical.organization.name == org_name and ref.canonical.identity_confidence == "medium", repo133 assert not any(r.predicate == "develops" and r.object is ref for r in facts.relations)134 # unsure → no invented model135 facts = Facts()136 ref = connector._model_ref(facts, "mradermacher/FooBar-GGUF", connector._org_ref(facts, "mradermacher", None))137 assert ref.entity_type == "artifact" and ref.canonical is None # redistributor, unknown family138 facts = Facts()139 ref = connector._model_ref(facts, "Qwen/Qwen3-8B", connector._org_ref(facts, "Qwen", None), quant_format="gguf")140 assert ref.entity_type == "artifact" and ref.canonical is None # quantised per tags only: nothing to strip from the name141142143async def test_daily_papers(connector):144 facts = await extract_from_fixture(connector, Target(url="https://huggingface.co/papers", doc_type="listing", key="papers"), fixture_path("huggingface", "papers.html"))145 papers = [e for e in facts.entities if e.entity_type == "paper"]146 assert len(papers) == 25 and all("arxiv" in p.identifiers for p in papers)147 first = claims_of(facts, papers[0].name)148 assert first["arxiv_id"] == "2609.10715" and first["metric.upvotes"] == 157 and first["pdf_url"] == "https://arxiv.org/pdf/2609.10715"149 assert len(first["authors"]) > 10 and first["published_at"].startswith("2026-09-09")150 assert entity_names(facts, "paper")151