Spaces:
Running
Running
Download src/eval_entity_registry/cli.py from evaleval/entity-registry: direct link, hf CLI and curl.
- Browser
- Download file 86.8 kB
-
https://huggingface.co/spaces/evaleval/entity-registry/resolve/main/src/eval_entity_registry/cli.py
- Command line
-
hf download hf://spaces/evaleval/entity-registry/src/eval_entity_registry/cli.py
-
curl -L -o cli.py https://huggingface.co/spaces/evaleval/entity-registry/resolve/main/src/eval_entity_registry/cli.py
86.8 kB
| """ | |
| eval-entity-registry CLI. | |
| Commands: | |
| seed Load known entities from seed/ YAML files | |
| stats Print registry summary | |
| sync Batch sync one or all EEE configs → eval_results table | |
| """ | |
| import json | |
| import math | |
| import re | |
| import unicodedata | |
| from numbers import Real | |
| from pathlib import Path | |
| from typing import Optional | |
| import typer | |
| import yaml | |
| # Source scope keys — the `source_config` on a scoped alias or a scoped | |
| # metric fold. A key is an UPSTREAM DATASET CONFIG NAME exactly as published | |
| # in the EEE datastore (`data/<config>/`): `openeval`, `llm-stats`, | |
| # `helm_capabilities`, … Registry ids are never scope keys, and the spelling | |
| # is not normalised — `llm-stats` and `llm_stats` are different configs. This | |
| # pattern is the shape the datastore uses, so a typo'd or path-like key fails | |
| # the seed instead of silently scoping an alias to a config that never calls. | |
| _SOURCE_SCOPE_KEY_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") | |
| def seed_collision_key(s: str) -> str: | |
| """The key under which two seed surface forms count as one entity: | |
| NFKD + combining-mark strip (so "racé" and "race" collide before the | |
| alphanumeric strip would silently drop the accent), casefold, and drop | |
| everything but [a-z0-9]. Used by the benchmark collision guard at seed | |
| time and by tests/test_seed_unique_ids.py for every flat seed file. | |
| Residual: cross-script confusables (Cyrillic 'а') still evade.""" | |
| decomposed = unicodedata.normalize("NFKD", str(s)) | |
| base = "".join(ch for ch in decomposed if not unicodedata.combining(ch)) | |
| return re.sub(r"[^a-z0-9]", "", base.casefold()) | |
| def _json_encode_if_needed(value): | |
| """Encode lists/dicts as JSON strings; pass through anything else. | |
| seed/models.yaml uses YAML-native lists for `tags` (e.g. `["open-weight"]`) | |
| while seed/benchmarks.yaml stores them pre-encoded as strings (e.g. | |
| `'["instruction-following"]'`). The canonical_* parquet columns are all | |
| VARCHAR, so we coerce on the way in to keep both formats supported. | |
| """ | |
| if isinstance(value, (list, dict)): | |
| return json.dumps(value) | |
| return value | |
| def _legacy_parent_model_id_to_parents(entry: dict) -> None: | |
| """Translate a legacy `parent_model_id: X` field to the typed `parents` | |
| list shape. Mutates the entry in place. | |
| Legacy core.yaml / sources/*.generated.yaml use a single scalar | |
| `parent_model_id` to express a family/variant relationship (e.g. | |
| Llama-3-8B → Llama-3). The new schema replaces this with a typed list | |
| of parent edges. This shim converts on load so existing YAML keeps | |
| working until each file is migrated to emit `parents` natively. | |
| No-op when `parents` is already present (new shape wins) or when neither | |
| field is set. | |
| """ | |
| if "parents" in entry and entry["parents"] is not None: | |
| entry.pop("parent_model_id", None) | |
| return | |
| legacy = entry.pop("parent_model_id", None) | |
| if legacy: | |
| entry["parents"] = [{"id": legacy, "relationship": "variant", "axis": "size"}] | |
| from eval_entity_registry.store.hf_store import get_store | |
| from eval_entity_registry.store import queries, schemas | |
| from eval_entity_registry.lib import collision_fold, org_attribution | |
| from eval_entity_registry.lib.seed_io import WEAK_SCALAR_FIELDS, build_hf_to_dev_from_orgs_yaml | |
| from eval_entity_registry.store.queries import _derive_release_date_from_id, _is_na | |
| from eval_entity_resolver.normalization import normalize as _normalize_alias | |
| from eval_entity_resolver.display import humanize_model_slug | |
| def _humanized_display(entry: dict) -> str: | |
| """Presentation display_name for a model, humanized at load time. | |
| This shapes ONLY the stored display column — NOT alias promotion — so display | |
| coverage stays fully DECOUPLED from resolution: the seed loop promotes the | |
| ORIGINAL display_name (pre-humanize) as an alias exactly as before, leaving | |
| every raw's resolved canonical unchanged. | |
| Rule: a display is a label if it contains a SPACE; otherwise it is a slug. | |
| - Empty display: humanize the id. | |
| - Slug display (no spaces — a raw id, a bare leaf like `miqu-1-70b-sf`, an | |
| org-qualified id like `meta-llama/Llama-3.1-8B-Instruct`, or a tier-3 raw | |
| `EVA-UNIT-01/eva-qwen2-5-32b-v0-2`): humanize it. In a slug every hyphen is | |
| a separator, so this is safe. | |
| - Label display (has spaces — curated/generator human names like | |
| `Jamba Large 1.6` or `Lingma Agent + Lingma SWE-GPT 72b (v0918)`): keep | |
| as-is. Humanizing here would mangle proper-noun hyphens (`SWE-GPT` -> | |
| `SWE GPT`). | |
| A leading `unknown/` placeholder-org prefix is dropped from the rendered | |
| label (`unknown` is the "real org not known" sentinel, never a display org). | |
| KNOWN LIMITATION: `humanize_model_slug` is a heuristic, so a handful of | |
| slug displays with intentional casing lose it (`QwQ-32B` -> `QWQ 32B`, | |
| `...-128E-Instruct` -> `...128e...`). These stay readable; fixing them means | |
| extending the shared humanizer, which also ripples into generator-output | |
| confluence, so it is deferred. | |
| """ | |
| cid = str(entry.get("id") or "") | |
| dn = str(entry.get("display_name") or "").strip() | |
| name = cid.split("/", 1)[1] if "/" in cid else cid # org-stripped id | |
| if not dn or dn == cid: | |
| # No real label — derive from the id. An org-stripped name that already | |
| # reads as a label (has spaces, e.g. `Amazon Q Developer Agent (...)`) | |
| # is used verbatim; a slug name is humanized. | |
| out = name if " " in name else humanize_model_slug(cid) | |
| elif " " not in dn: | |
| out = humanize_model_slug(dn) | |
| else: | |
| out = dn | |
| # Drop a leading `unknown/` placeholder-org prefix. The slug/id branches | |
| # already strip it via the org split; this catches the space-containing | |
| # curated labels (e.g. `unknown/Lingma Agent + ...`). | |
| if out.startswith("unknown/"): | |
| out = out[len("unknown/"):] | |
| return out | |
| app = typer.Typer(help="eval-entity-registry CLI") | |
| def _load_store(): | |
| store = get_store() | |
| if not store.loaded: | |
| store.load() | |
| return store | |
| # ------------------------------------------------------------------ | |
| # seed | |
| # ------------------------------------------------------------------ | |
| def seed( | |
| local: bool = typer.Option(False, "--local", help="Write to fixtures/ instead of HF Hub"), | |
| seed_dir: str = typer.Option("./seed", "--seed-dir"), | |
| prune_stale: bool = typer.Option( | |
| False, | |
| "--prune-stale/--no-prune-stale", | |
| help="Remove reviewed seed entities and seed aliases absent from the current YAML snapshot.", | |
| ), | |
| ): | |
| """Load known canonical entities from seed YAML files.""" | |
| import os | |
| if local: | |
| os.environ["LOCAL_MODE"] = "true" | |
| store = _load_store() | |
| seed_path = Path(seed_dir) | |
| # ------------------------------------------------------------------ | |
| # Models — three-layer load from seed/models/: | |
| # sources/*.generated.yaml → external catalog data (e.g. models.dev), | |
| # flat lists, never hand-edited | |
| # core.yaml → curated canonicals (the source of truth), | |
| # flat list OR {skip_ids, entries} dict | |
| # enrichments/aliases.yaml → optional alias-only entries ({id, aliases}) | |
| # that union onto whatever exists | |
| # | |
| # Merge order: sources → core → enrichments. Field-level merge per entry | |
| # (aliases / tags UNION; other scalars prefer non-empty, last-write-wins). | |
| # `skip_ids` from core drops generated entries we don't want. | |
| # Source enrich records may carry a `weak:` scalar map (donated from a | |
| # suppressed/folded mint); weak values are applied LAST, filling only | |
| # fields every strong merge left empty — see the weak-fill block below. | |
| # ------------------------------------------------------------------ | |
| def _load_models_merged() -> list[dict]: | |
| models_dir = seed_path / "models" | |
| sources_dir = models_dir / "sources" | |
| core_file = models_dir / "core.yaml" | |
| enrichments_dir = models_dir / "enrichments" | |
| source_entries: list[dict] = [] | |
| core_entries: list[dict] = [] | |
| enrichment_entries: list[dict] = [] | |
| skip_ids: set[str] = set() | |
| def _is_empty(v) -> bool: | |
| if v is None: | |
| return True | |
| if isinstance(v, (list, dict)) and len(v) == 0: | |
| return True | |
| if isinstance(v, str) and v.strip() in ("", "[]", "{}"): | |
| return True | |
| return False | |
| # WEAK scalar contributions: a generated enrich record may carry a | |
| # `weak: {field: value}` map (scalars donated from a suppressed/folded | |
| # mint by scripts/refresh_from_modelsdev.py). Pulled out at load so | |
| # they never enter the strong field merge; applied after ALL strong | |
| # merges (see the weak-fill block below). Collected in deterministic | |
| # order: source files in sorted-filename order, records in file order. | |
| weak_candidates: list[tuple[str, str, object]] = [] | |
| if sources_dir.is_dir(): | |
| for src_path in sorted(sources_dir.glob("*.generated.yaml")): | |
| with open(src_path) as f: | |
| loaded = yaml.safe_load(f) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{src_path} must be a flat list") | |
| for entry in loaded: | |
| if not isinstance(entry, dict) or not entry.get("id"): | |
| continue | |
| weak = entry.pop("weak", None) | |
| if isinstance(weak, dict): | |
| for field in WEAK_SCALAR_FIELDS: | |
| if not _is_empty(weak.get(field)): | |
| weak_candidates.append((entry["id"], field, weak[field])) | |
| source_entries.extend(loaded) | |
| skip_source_ids: set[str] = set() | |
| if core_file.exists(): | |
| with open(core_file) as f: | |
| loaded = yaml.safe_load(f) or {} | |
| if isinstance(loaded, list): | |
| core_entries = loaded | |
| elif isinstance(loaded, dict): | |
| core_entries = loaded.get("entries", []) or [] | |
| skip_ids = set(loaded.get("skip_ids", []) or []) | |
| # `skip_source_ids` drops these ids from sources/enrichments only, | |
| # leaving core entries authoritative. Used when models.dev (or any | |
| # auto-generated source) ships bad aliases for a model that core.yaml | |
| # curates correctly — otherwise the loader's UNION-merge would | |
| # re-introduce the bad aliases on every refresh. | |
| skip_source_ids = set(loaded.get("skip_source_ids", []) or []) | |
| else: | |
| raise typer.BadParameter(f"{core_file} unexpected shape {type(loaded)}") | |
| # Keys EXPLICITLY present on each core entry — even with a null value. | |
| # An explicit core null (e.g. `open_weights: null` where the upstream | |
| # catalog's value is known-wrong) is a curated "unknown" and blocks | |
| # weak fill on that field; weak fills only keys core leaves ABSENT. | |
| core_explicit: dict[str, set[str]] = {} | |
| for entry in core_entries: | |
| if isinstance(entry, dict) and entry.get("id"): | |
| core_explicit.setdefault(entry["id"], set()).update(entry.keys()) | |
| # Enrichment overlays: every enrichments/*.yaml (flat list of {id, ...}), | |
| # field-merged onto the matching canonical (aliases + parents UNION; see | |
| # _merge_into). Separate files keep concerns apart — aliases.yaml carries | |
| # alias bridges, parents.yaml the curated typed-edge graph (lineage edges | |
| # the bulk generators can't reconstruct from their source data). | |
| if enrichments_dir.is_dir(): | |
| for enr_path in sorted(enrichments_dir.glob("*.yaml")): | |
| with open(enr_path) as f: | |
| loaded = yaml.safe_load(f) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{enr_path} must be a flat list") | |
| enrichment_entries.extend(loaded) | |
| def _merge_into(target: dict, src: dict) -> dict: | |
| """Merge two entries with the same canonical_id. | |
| Field-level merge policy: | |
| - `aliases`: UNION (case-insensitive dedup). | |
| - `tags`: UNION (case-insensitive dedup). Both YAML-list and | |
| JSON-encoded-string forms supported. Protects against session | |
| additions overwriting `[open-weight, moe]` with `[open-weight]`. | |
| - `metadata`: per-KEY merge of the two JSON objects (later source | |
| wins per key, no key ever dropped). Protects against e.g. | |
| models_dev_catalog's `{alias_platforms}` wiping hub_stats' | |
| `{hf_id, downloads_all_time, ...}`. Falls back to the scalar | |
| rule when either side isn't a JSON object. | |
| - Other scalars: prefer non-empty across the pair; when both | |
| sides have a non-empty value, last-write-wins. Protects against | |
| session-batch entries that omit `architecture` / | |
| `params_billions` from silently overwriting earlier rich entries. | |
| "Empty" means: None, "", [], {}, or default-looking '{}' / '[]'. | |
| """ | |
| import json as _json | |
| existing_aliases = list(target.get("aliases") or []) | |
| existing_lc = {a.lower() for a in existing_aliases if a} | |
| new_aliases = list(src.get("aliases") or []) | |
| for a in new_aliases: | |
| if a and a.lower() not in existing_lc: | |
| existing_aliases.append(a) | |
| existing_lc.add(a.lower()) | |
| def _decode_list_field(v): | |
| """tags / metadata may be either YAML-list or JSON-encoded | |
| string. Return a list (best-effort) and a boolean indicating | |
| whether to re-encode on write.""" | |
| if v is None: | |
| return [], False | |
| if isinstance(v, list): | |
| return list(v), False | |
| if isinstance(v, str): | |
| s = v.strip() | |
| if not s or s in ("[]", "null"): | |
| return [], True | |
| try: | |
| d = _json.loads(s) | |
| if isinstance(d, list): | |
| return list(d), True | |
| except (ValueError, TypeError): | |
| pass | |
| return [v], False | |
| # Union tags (handles both list and JSON-string formats) | |
| tgt_tags, tgt_was_json = _decode_list_field(target.get("tags")) | |
| src_tags, src_was_json = _decode_list_field(src.get("tags")) | |
| seen_tags_lc = {str(t).lower() for t in tgt_tags} | |
| for t in src_tags: | |
| if t is not None and str(t).lower() not in seen_tags_lc: | |
| tgt_tags.append(t) | |
| seen_tags_lc.add(str(t).lower()) | |
| # Re-encode if either source was a JSON string (the parquet column | |
| # is VARCHAR; _json_encode_if_needed downstream handles either). | |
| tags_merged = _json.dumps(tgt_tags) if (tgt_was_json or src_was_json) else tgt_tags | |
| # Union `parents` by id. For an edge present in both, field-merge | |
| # within the edge so a later source can fill in `axis` (or correct | |
| # `relationship`) without duplicating the edge. Edges from the | |
| # target preserve their order; new edges from src are appended. | |
| tgt_parents, tgt_p_was_json = _decode_list_field(target.get("parents")) | |
| src_parents, src_p_was_json = _decode_list_field(src.get("parents")) | |
| parents_by_id: dict[str, dict] = {} | |
| parents_order: list[str] = [] | |
| for p in tgt_parents: | |
| if isinstance(p, dict) and p.get("id"): | |
| pid = p["id"] | |
| if pid not in parents_by_id: | |
| parents_order.append(pid) | |
| parents_by_id[pid] = dict(p) | |
| for p in src_parents: | |
| if not isinstance(p, dict) or not p.get("id"): | |
| continue | |
| pid = p["id"] | |
| if pid in parents_by_id: | |
| merged_edge = dict(parents_by_id[pid]) | |
| for k, v in p.items(): | |
| if _is_empty(v): | |
| continue | |
| merged_edge[k] = v | |
| parents_by_id[pid] = merged_edge | |
| else: | |
| parents_order.append(pid) | |
| parents_by_id[pid] = dict(p) | |
| parents_list = [parents_by_id[pid] for pid in parents_order] | |
| parents_merged = ( | |
| _json.dumps(parents_list) | |
| if (tgt_p_was_json or src_p_was_json) | |
| else parents_list | |
| ) | |
| # Per-key metadata merge. Both sides may be a dict or a | |
| # JSON-encoded string; malformed/non-dict input opts that pair out | |
| # (handled by the scalar last-non-empty-wins loop below instead). | |
| def _decode_dict_field(v): | |
| """Return (dict, was_json, is_dict).""" | |
| if v is None: | |
| return {}, False, True | |
| if isinstance(v, dict): | |
| return dict(v), False, True | |
| if isinstance(v, str): | |
| s = v.strip() | |
| if not s or s in ("{}", "null"): | |
| return {}, True, True | |
| try: | |
| d = _json.loads(s) | |
| if isinstance(d, dict): | |
| return d, True, True | |
| except (ValueError, TypeError): | |
| pass | |
| return {}, False, False | |
| tgt_meta, tgt_m_json, tgt_m_ok = _decode_dict_field(target.get("metadata")) | |
| src_meta, src_m_json, src_m_ok = _decode_dict_field(src.get("metadata")) | |
| metadata_handled = tgt_m_ok and src_m_ok | |
| if metadata_handled: | |
| meta_merged = {**tgt_meta, **src_meta} # later source wins per key | |
| metadata_merged = ( | |
| _json.dumps(meta_merged, sort_keys=True) | |
| if (tgt_m_json or src_m_json) | |
| else meta_merged | |
| ) | |
| merged = dict(target) | |
| for k, v in src.items(): | |
| if k in ("aliases", "tags", "parents"): | |
| continue # handled separately | |
| if k == "metadata" and metadata_handled: | |
| continue # handled separately | |
| if _is_empty(v): | |
| continue | |
| merged[k] = v | |
| if metadata_handled and ("metadata" in target or "metadata" in src): | |
| merged["metadata"] = metadata_merged | |
| merged["aliases"] = existing_aliases | |
| merged["tags"] = tags_merged | |
| # Only emit `parents` if at least one side had any (avoids creating | |
| # a spurious empty list on entries that never had a parents field). | |
| if tgt_parents or src_parents: | |
| merged["parents"] = parents_merged | |
| return merged | |
| by_id: dict[str, dict] = {} | |
| def _absorb(entries: list[dict], extra_skip: set[str] = frozenset()) -> None: | |
| drop = skip_ids | extra_skip | |
| for e in entries: | |
| if "id" not in e: | |
| raise typer.BadParameter(f"models seed entry missing id: {e!r}") | |
| if e["id"] in drop: | |
| continue | |
| # Translate legacy `parent_model_id` scalar to the typed | |
| # `parents` list before any merge / column-filter step. | |
| _legacy_parent_model_id_to_parents(e) | |
| if e["id"] in by_id: | |
| by_id[e["id"]] = _merge_into(by_id[e["id"]], e) | |
| else: | |
| by_id[e["id"]] = e | |
| # Sources/enrichments respect both skip_ids and skip_source_ids; | |
| # core entries respect only skip_ids so curated overrides always apply. | |
| _absorb(source_entries, extra_skip=skip_source_ids) | |
| _absorb(core_entries) | |
| _absorb(enrichment_entries, extra_skip=skip_source_ids) | |
| merged = list(by_id.values()) | |
| # Attribute the developer org to malformed-org draft ids (no '/', org | |
| # glued by -/.) so genuine models aren't orphaned from their developer | |
| # (cohere-march-2024 -> org cohere; nvidia.nemotron-* -> nvidia/...). | |
| # Runs BEFORE the fold so any normalised id can collapse with its twin. | |
| merged, _org_merges = org_attribution.attribute_orgs( | |
| merged, build_hf_to_dev_from_orgs_yaml(seed_path / "orgs.yaml") | |
| ) | |
| # Fold normalize-collisions: the SAME model minted under different | |
| # separator spellings (gemini-1.5-pro vs gemini-1-5-pro, gpt-5.2 vs the | |
| # venice relabel gpt-52) collapses into ONE canonical so it surfaces as | |
| # one page. Guarded against false size merges (opt-1.3b != opt-13b); a | |
| # curated collision_overrides.yaml carries the do-not-fold list + winner | |
| # pins. (See lib/collision_fold.py.) | |
| ov_file = models_dir / "collision_overrides.yaml" | |
| never_fold, prefer, curated_merge, non_lineage_bases = [], {}, {}, set() | |
| if ov_file.is_file(): | |
| ov = yaml.safe_load(ov_file.read_text()) or {} | |
| never_fold = ov.get("never_fold") or [] | |
| prefer = ov.get("prefer") or {} | |
| # `merge`: curated {loser_id -> HF-true winner_id} for same-model | |
| # cross-namespace spellings the automatic fold can't key together | |
| # (different org prefix, or a `1-2b`-vs-`1.2B` size-guard false block). | |
| curated_merge = ov.get("merge") or {} | |
| # `non_lineage_bases`: underspecified umbrella ids that stay resolvable | |
| # but may never be a lineage PARENT (see collision_overrides.yaml). | |
| non_lineage_bases = set(ov.get("non_lineage_bases") or []) | |
| # An HF-true canonical (oracle-sourced, or HF-promoted by the models.dev | |
| # refresh with `hf_deferred`) carries the invented ids it replaced as | |
| # aliases. A side source the refresh never rewrites (enrichments, | |
| # tier3, hub_stats) may still key a record by such an id; left alone it | |
| # materialises a second canonical that collides on the alias. Fold | |
| # those records onto the HF entry, like a curated `merge` (core ids are | |
| # never folded this way). | |
| core_ids = {e["id"] for e in core_entries} | |
| # A multi-child family root (`alibaba/qwen3-vl-235b-a22b` over Instruct | |
| # and Thinking) is not one repo and stays a root, like an umbrella. | |
| kids: dict[str, set[str]] = {} | |
| for e in merged: | |
| for p in collision_fold._edges(e.get("parents")): | |
| if not isinstance(p, dict) or p.get("relationship") != "variant": | |
| continue | |
| pid = p.get("id") | |
| if isinstance(pid, str) and pid != e["id"]: | |
| kids.setdefault(pid, set()).add(e["id"]) | |
| multi_child = {pid for pid, k in kids.items() if len(k) >= 2} | |
| def _own_name(old: str, hf_id: str) -> bool: | |
| return seed_collision_key(old.rsplit("/", 1)[-1]) == seed_collision_key(hf_id.rsplit("/", 1)[-1]) | |
| def _hf_true(e: dict) -> bool: | |
| meta = e.get("metadata") | |
| if isinstance(meta, str): | |
| try: | |
| meta = json.loads(meta) | |
| except ValueError: | |
| meta = None | |
| return e.get("resolution_source") == "hf" or ( | |
| isinstance(meta, dict) and meta.get("hf_deferred") is True | |
| ) | |
| hf_true_ids = {e["id"] for e in merged if _hf_true(e)} | |
| core_keys = {collision_fold.collision_key(i) for i in core_ids} | |
| promoted_merge: dict[str, str] = {} | |
| for e in merged: | |
| if e["id"] not in hf_true_ids: | |
| continue | |
| for a in e.get("aliases") or []: | |
| # Never a core id, a curated umbrella, another HF-true | |
| # canonical, or a multi-child root under a different name. | |
| if a in core_ids or a in non_lineage_bases or a in hf_true_ids: | |
| continue | |
| if a in multi_child and not _own_name(a, e["id"]): | |
| continue | |
| if a != e["id"] and a in by_id: | |
| promoted_merge.setdefault(a, e["id"]) | |
| # A loser that is itself a declared winner would make a cycle. | |
| promoted_merge = {l: w for l, w in promoted_merge.items() if w not in promoted_merge} | |
| for l, w in promoted_merge.items(): | |
| # A casing twin shares the collision key; pin the HF id as that | |
| # group's winner so the group fold cannot pick the alias and leave | |
| # the two remaps pointing at each other. A key a core entry owns | |
| # keeps its core winner. | |
| key = collision_fold.collision_key(w) | |
| if collision_fold.collision_key(l) == key and key not in core_keys: | |
| prefer.setdefault(key, w) | |
| merged, _remap = collision_fold.fold_collisions( | |
| merged, never_fold, prefer, | |
| force_merge={**_org_merges, **promoted_merge, **curated_merge}, | |
| non_lineage_bases=non_lineage_bases, | |
| ) | |
| # WEAK FILL — LAST, after every strong merge INCLUDING the collision | |
| # fold (a folded-in full entry's scalars are strong too), so a value | |
| # from any full entry beats a weak one regardless of file load order. | |
| # A weak value lands only when the merged field is still empty AND the | |
| # key is not explicitly present on the core entry (an explicit core | |
| # null is a curated unknown — see core_explicit above). Two weak values | |
| # for the same (id, field): the FIRST contribution wins — source files | |
| # in sorted-filename order, records in file order (weak_candidates). | |
| weak_scalars: dict[tuple[str, str], object] = {} | |
| for eid, field, value in weak_candidates: | |
| if eid in skip_ids or eid in skip_source_ids: | |
| continue | |
| weak_scalars.setdefault((_remap.get(eid, eid), field), value) | |
| by_merged_id = {e["id"]: e for e in merged} | |
| for (eid, field), value in weak_scalars.items(): | |
| target = by_merged_id.get(eid) | |
| if target is None or field in core_explicit.get(eid, ()): | |
| continue | |
| # A dated-snapshot id's parsed date outranks weak data: the | |
| # seed-time derive fallback (`release_date_derived_from_id`) would | |
| # fill it anyway, and the id stamp is the snapshot's own identity. | |
| if field == "release_date" and _derive_release_date_from_id(eid) is not None: | |
| continue | |
| if _is_empty(target.get(field)): | |
| target[field] = value | |
| return merged | |
| # ------------------------------------------------------------------ | |
| # Benchmarks — two-source load: | |
| # seed/benchmarks.yaml → curated canonicals (the | |
| # source of truth, hand-edited) | |
| # seed/benchmarks_generated/*.yaml → bulk auto-generated entries | |
| # (e.g. AIR-Bench 2024's 373 | |
| # categories, derived from | |
| # scripts/data/air_bench_2024_raw_strings.txt) | |
| # | |
| # Merge order: generated → curated. Field-level merge per id (aliases | |
| # union; other scalars prefer non-empty, last-write-wins) so curated | |
| # entries can refine an auto-generated row without losing its aliases. | |
| # Generator scripts must use stable canonical_ids so refreshes are | |
| # idempotent. | |
| # ------------------------------------------------------------------ | |
| def _load_benchmarks_merged() -> list[dict]: | |
| curated_path = seed_path / "benchmarks.yaml" | |
| generated_dir = seed_path / "benchmarks_generated" | |
| def _load_benchmarks_yaml(path: Path) -> list[dict]: | |
| raw_yaml = path.read_text() | |
| invalid_judge_flag = re.search( | |
| r"^\s*preferred_metric_llm_judged:\s*(?!true\b|false\b|null\b|~)(?P<value>\S+)", | |
| raw_yaml, | |
| re.MULTILINE, | |
| ) | |
| if invalid_judge_flag: | |
| raise typer.BadParameter( | |
| f"{path}: invalid preferred_metric_llm_judged value " | |
| f"{invalid_judge_flag.group('value')!r}; " | |
| "use true, false, null, or ~" | |
| ) | |
| loaded = yaml.safe_load(raw_yaml) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{path} must be a flat list") | |
| return loaded | |
| generated_entries: list[dict] = [] | |
| if generated_dir.is_dir(): | |
| for src_path in sorted(generated_dir.glob("*.yaml")): | |
| generated_entries.extend(_load_benchmarks_yaml(src_path)) | |
| curated_entries: list[dict] = [] | |
| if curated_path.exists(): | |
| curated_entries = _load_benchmarks_yaml(curated_path) | |
| def _merge_benchmark(generated: dict, curated: dict) -> dict: | |
| """Curated wins on every field it specifies; aliases are | |
| unioned (case-insensitive dedup) so generator-emitted aliases | |
| survive even when curated narrows the entry.""" | |
| merged = dict(generated) | |
| for k, v in curated.items(): | |
| if k == "aliases": | |
| continue | |
| merged[k] = v | |
| existing = list(generated.get("aliases") or []) | |
| existing_lc = {a.lower() for a in existing if a} | |
| for a in (curated.get("aliases") or []): | |
| if a and a.lower() not in existing_lc: | |
| existing.append(a) | |
| existing_lc.add(a.lower()) | |
| merged["aliases"] = existing | |
| return merged | |
| by_id: dict[str, dict] = {} | |
| for entry in generated_entries: | |
| if "id" not in entry: | |
| raise typer.BadParameter(f"benchmarks generated entry missing id: {entry!r}") | |
| by_id[entry["id"]] = entry | |
| for entry in curated_entries: | |
| if "id" not in entry: | |
| raise typer.BadParameter(f"benchmarks seed entry missing id: {entry!r}") | |
| if entry["id"] in by_id: | |
| by_id[entry["id"]] = _merge_benchmark(by_id[entry["id"]], entry) | |
| else: | |
| by_id[entry["id"]] = entry | |
| # `preferred_metric` (seed key) -> `preferred_metric_id` (column), | |
| # validated against metrics.yaml so a typo can't silently null the | |
| # merged-view default. | |
| metrics_path = seed_path / "metrics.yaml" | |
| metric_ids: set[str] = set() | |
| if metrics_path.exists(): | |
| with open(metrics_path) as f: | |
| metric_ids = {m["id"] for m in (yaml.safe_load(f) or [])} | |
| for entry in by_id.values(): | |
| pm = entry.pop("preferred_metric", None) | |
| judged = entry.get("preferred_metric_llm_judged") | |
| if judged is not None and type(judged) is not bool: | |
| raise typer.BadParameter( | |
| f"benchmark {entry['id']!r}: preferred_metric_llm_judged " | |
| "must be a boolean or null" | |
| ) | |
| if judged is not None and pm is None: | |
| raise typer.BadParameter( | |
| f"benchmark {entry['id']!r}: preferred_metric_llm_judged " | |
| "requires preferred_metric" | |
| ) | |
| if pm is not None: | |
| if pm not in metric_ids: | |
| raise typer.BadParameter( | |
| f"benchmark {entry['id']!r}: preferred_metric {pm!r} " | |
| f"is not a canonical metric id" | |
| ) | |
| entry["preferred_metric_id"] = pm | |
| _check_benchmark_collisions(list(by_id.values())) | |
| return list(by_id.values()) | |
| def _check_benchmark_collisions(entries: list[dict]) -> None: | |
| """Seed-time guard: two benchmarks whose ids, display names, or | |
| global aliases collapse to the same normalized key (casefold + | |
| strip non-alphanumerics — stricter than collision_fold's | |
| separator-only key, so spaced llm-stats-mint aliases like | |
| "Vita Bench" are covered) are one entity split in two — fold them | |
| (specs/benchmark-relationships-audit.md). Flag-only, never | |
| auto-folds. Groups verified genuinely distinct go in | |
| seed/benchmarks_distinct_allowlist.yaml (a list of id groups). | |
| Residual: semantic dupes with different normalized keys | |
| (mmmu-val vs mmmu-validation) are invisible to this check. | |
| Also rejects '/' in benchmark ids — reserved by the merged-view | |
| URL scheme (single-segment merged eval ids stay collision-free | |
| with two-segment per-source ids only while benchmark ids are | |
| slash-free).""" | |
| _norm = seed_collision_key | |
| allow_path = seed_path / "benchmarks_distinct_allowlist.yaml" | |
| allowed: list[frozenset[str]] = [] | |
| if allow_path.exists(): | |
| with open(allow_path) as f: | |
| for group in yaml.safe_load(f) or []: | |
| allowed.append(frozenset(group)) | |
| # Typo guard: every allowlisted id must name a real benchmark, | |
| # else a stale/misspelled allowlist entry silently stops guarding. | |
| all_ids = {str(e["id"]) for e in entries} | |
| stale = sorted({bid for grp in allowed for bid in grp} - all_ids) | |
| if stale: | |
| raise typer.BadParameter( | |
| f"benchmarks_distinct_allowlist.yaml names unknown benchmark id(s): {stale}" | |
| ) | |
| key_owners: dict[str, set[str]] = {} | |
| for e in entries: | |
| bid = str(e["id"]) | |
| if "/" in bid: | |
| raise typer.BadParameter( | |
| f"benchmark id {bid!r} contains '/' (reserved for merged-view routing)" | |
| ) | |
| # id + display_name + aliases: all three become resolver | |
| # aliases at seed time (display_name is auto-promoted), so all | |
| # three can carry a dupe in. | |
| keys = {_norm(bid)} | |
| if e.get("display_name"): | |
| keys.add(_norm(e["display_name"])) | |
| keys |= {_norm(a) for a in (e.get("aliases") or []) if a} | |
| keys.discard("") | |
| for k in keys: | |
| key_owners.setdefault(k, set()).add(bid) | |
| conflicts = [ | |
| sorted(ids) | |
| for ids in key_owners.values() | |
| if len(ids) > 1 and not any(ids <= grp for grp in allowed) | |
| ] | |
| if conflicts: | |
| listing = "; ".join(str(g) for g in sorted(conflicts)) | |
| raise typer.BadParameter( | |
| f"{len(conflicts)} separator/case-collision group(s) among benchmark " | |
| f"ids+aliases — fold them in the seed or allowlist in " | |
| f"benchmarks_distinct_allowlist.yaml: {listing}" | |
| ) | |
| # ------------------------------------------------------------------ | |
| # Families — translate seed/families.yaml's nested {slug: {fields}} | |
| # shape into flat dicts ready for upsert. The YAML uses the slug as | |
| # the mapping key for human friendliness (`mmlu:` reads as a header); | |
| # the table needs `id` as a column. | |
| # | |
| # Output schema mirrors `canonical_families`: list-valued fields | |
| # (`benchmark_ids`, `folder_aliases`, `composite_keys`) are | |
| # JSON-encoded so they round-trip through the parquet StringDtype | |
| # column without losing structure. | |
| # ------------------------------------------------------------------ | |
| def _load_families_seed() -> list[dict]: | |
| path = seed_path / "families.yaml" | |
| if not path.exists(): | |
| return [] | |
| with open(path) as f: | |
| raw = yaml.safe_load(f) or {} | |
| if not isinstance(raw, dict): | |
| raise typer.BadParameter(f"{path} must be a top-level mapping {{slug: {{...}}}}") | |
| out: list[dict] = [] | |
| # Validation: each benchmark may only appear in one curated family. | |
| seen_benchmarks: dict[str, str] = {} | |
| for slug, fields in raw.items(): | |
| if not isinstance(fields, dict): | |
| raise typer.BadParameter(f"family {slug!r} entry must be a mapping, got {type(fields).__name__}") | |
| benchmark_ids = list(fields.get("benchmarks") or []) | |
| for bid in benchmark_ids: | |
| if bid in seen_benchmarks and seen_benchmarks[bid] != slug: | |
| raise typer.BadParameter( | |
| f"benchmark {bid!r} listed in two families: " | |
| f"{seen_benchmarks[bid]!r} and {slug!r}" | |
| ) | |
| seen_benchmarks[bid] = slug | |
| entry = { | |
| "id": slug, | |
| "display_name": fields.get("display") or slug, | |
| "category": fields.get("category"), | |
| "benchmark_ids": benchmark_ids, | |
| "primary_benchmark_key": fields.get("primary_benchmark_key"), | |
| "folder_aliases": list(fields.get("folder_aliases") or []), | |
| "composite_keys": list(fields.get("composite_keys") or []), | |
| "tags": fields.get("tags") or [], | |
| "metadata": fields.get("metadata") or {}, | |
| "review_status": fields.get("review_status") or "reviewed", | |
| } | |
| out.append(entry) | |
| return out | |
| # ------------------------------------------------------------------ | |
| # Composites — same translation as families. YAML shape: | |
| # {slug: {display, configs: [...], category?, family_id?}} | |
| # ------------------------------------------------------------------ | |
| def _load_composites_seed() -> list[dict]: | |
| path = seed_path / "composites.yaml" | |
| if not path.exists(): | |
| return [] | |
| with open(path) as f: | |
| raw = yaml.safe_load(f) or {} | |
| if not isinstance(raw, dict): | |
| raise typer.BadParameter(f"{path} must be a top-level mapping {{slug: {{...}}}}") | |
| out: list[dict] = [] | |
| for slug, fields in raw.items(): | |
| if not isinstance(fields, dict): | |
| raise typer.BadParameter(f"composite {slug!r} entry must be a mapping, got {type(fields).__name__}") | |
| raw_configs = fields.get("configs") | |
| if raw_configs is None: | |
| # Display-only override (no explicit `configs:`): implicit | |
| # single source_config equal to the slug. Some upstream | |
| # EEE folders are kebab (`arc-agi`), others snake | |
| # (`helm_classic`); ship both forms so the producer's | |
| # composite_config_map JOIN matches whichever the data | |
| # uses. De-dup when slug has no `-`. | |
| kebab = slug | |
| snake = slug.replace("-", "_") | |
| source_configs = [kebab] if kebab == snake else [kebab, snake] | |
| else: | |
| # Members are bare config strings or scoped mappings | |
| # {config, org[, source]} (composite-partition spec). | |
| # Scoped mappings pass through as objects — the mixed | |
| # list is JSON-encoded below by _json_encode_if_needed; | |
| # str() would ship Python-repr garbage. | |
| source_configs = [] | |
| for c in raw_configs: | |
| if isinstance(c, dict): | |
| unknown = sorted(set(c) - {"config", "org", "source"}) | |
| if unknown or "config" not in c: | |
| raise typer.BadParameter( | |
| f"composite {slug!r}: scoped configs member " | |
| f"{c!r} must have keys {{config, org[, source]}}" | |
| ) | |
| if c.get("source") is not None and c.get("org") is None: | |
| raise typer.BadParameter( | |
| f"composite {slug!r}: scoped member {c!r} " | |
| f"sets `source` without `org`" | |
| ) | |
| member = {"config": str(c["config"])} | |
| if c.get("org") is not None: | |
| member["org"] = str(c["org"]) | |
| if c.get("source") is not None: | |
| member["source"] = str(c["source"]) | |
| source_configs.append(member) | |
| else: | |
| source_configs.append(str(c)) | |
| entry = { | |
| "id": slug, | |
| "display_name": fields.get("display") or slug, | |
| "category": fields.get("category"), | |
| "source_configs": source_configs, | |
| "family_id": fields.get("family_id"), | |
| "tags": fields.get("tags") or [], | |
| "metadata": fields.get("metadata") or {}, | |
| "review_status": fields.get("review_status") or "reviewed", | |
| } | |
| out.append(entry) | |
| return out | |
| # ------------------------------------------------------------------ | |
| # Orgs — two-file load: | |
| # seed/orgs.yaml → curated first-party labs (the source | |
| # of truth, hand-edited) | |
| # seed/orgs.generated.yaml → auto-created orgs from hub-stats refresh | |
| # (HF authors that aren't curated labs) | |
| # | |
| # Curated wins on id collision. Unlike the models merge (field-level), | |
| # orgs use a simple "drop generated entry if id is in curated" policy: | |
| # curated entries are deliberate and richer; auto-created entries are | |
| # thin (just id, display_name, kind=unknown), so a partial overlay | |
| # would never improve the curated record. | |
| # ------------------------------------------------------------------ | |
| def _load_orgs_merged() -> list[dict]: | |
| curated_path = seed_path / "orgs.yaml" | |
| generated_path = seed_path / "orgs.generated.yaml" | |
| curated: list[dict] = [] | |
| if curated_path.exists(): | |
| with open(curated_path) as f: | |
| loaded = yaml.safe_load(f) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{curated_path} must be a flat list") | |
| curated = loaded | |
| generated: list[dict] = [] | |
| if generated_path.exists(): | |
| with open(generated_path) as f: | |
| loaded = yaml.safe_load(f) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{generated_path} must be a flat list") | |
| generated = loaded | |
| curated_ids = {e["id"] for e in curated if "id" in e} | |
| out = list(curated) | |
| for e in generated: | |
| if "id" not in e: | |
| raise typer.BadParameter(f"orgs.generated.yaml entry missing id: {e!r}") | |
| if e["id"] not in curated_ids: | |
| out.append(e) | |
| return out | |
| # table name, yaml file, label, entity_type (for alias creation) | |
| seed_specs = [ | |
| # Orgs: load via merge helper to combine curated + auto-generated. | |
| ("canonical_orgs", "__merged_orgs__", "orgs", "org"), | |
| # Benchmarks: load via merge helper. Curated entries live in | |
| # seed/benchmarks.yaml; bulk-generated entries (e.g. AIR-Bench | |
| # 2024's 373 categories from the refresh script) live in | |
| # seed/benchmarks_generated/*.yaml. Sentinel path triggers the | |
| # _load_benchmarks_merged() helper. | |
| ("canonical_benchmarks", "__merged_benchmarks__", "benchmarks", "benchmark"), | |
| ("canonical_metrics", seed_path / "metrics.yaml", "metrics", "metric"), | |
| ("eval_harnesses", seed_path / "harnesses.yaml", "harnesses", "harness"), | |
| # Families & composites are first-class registry entities. Their | |
| # YAML uses {slug: {...}} shape, so we need translation loaders | |
| # rather than the flat-list path. | |
| # entity_type='family'/'composite' aliases are emitted for | |
| # consistency but aren't consulted by the resolver today. | |
| ("canonical_families", "__nested_families__", "families", "family"), | |
| ("canonical_composites", "__nested_composites__", "composites", "composite"), | |
| # Models: load via the merge helper; pass a sentinel path that | |
| # signals the loop below to invoke _load_models_merged() instead of | |
| # reading a single YAML file. | |
| ("canonical_models", "__merged_models__", "models", "model"), | |
| ] | |
| alias_count = 0 | |
| # Distinct source scope keys (EEE dataset config names) seen across | |
| # scoped aliases and scoped metric folds — reported in the seed summary | |
| # so a typo'd config shows up as a new key rather than a silent no-op. | |
| source_scope_keys: set[str] = set() | |
| # Track all seed entity IDs and alias keys so we can remove stale ones. | |
| # Alias key: (raw_value, entity_type, canonical_id, source_config) | |
| seed_snapshot: list[tuple[str, str, set[str], set[tuple[str, str, str, Optional[str]]]]] = [] | |
| # Detect same-run alias collisions: two DIFFERENT canonicals declaring the | |
| # same (raw_value, entity_type, source_config) within ONE seed pass. The | |
| # add_alias/repoint path below silently last-write-wins on these, so the | |
| # owner is seed-order-dependent (nondeterministic) — a real data bug. We | |
| # record them here and raise after the loop so dirty seed data can't ship | |
| # silently. NOTE: a legitimate YAML *rename* (the persisted store had the | |
| # alias on a canonical that no longer claims it) is NOT a collision — only | |
| # same-pass double-claims are, which is why this is keyed on this run only. | |
| # Values/rows carry (owner, declarer) pairs: `owner` is the post-redirect | |
| # target the row will point at; `declarer` is the entity whose alias list | |
| # actually claimed the raw (what the operator must edit to resolve it). | |
| run_alias_owners: dict[tuple[str, str, Optional[str]], tuple[str, str]] = {} | |
| alias_collisions: list[tuple[str, str, Optional[str], str, str, str, str]] = [] | |
| # Build the alias index once so add_alias collision checks are O(1) instead | |
| # of O(N) DataFrame mask scans. Combined with buffered=True below, this | |
| # avoids the O(N²) pd.concat-per-row cost on ~1k entities + ~13k aliases. | |
| queries._rebuild_alias_index(store) | |
| for table, yaml_file, label, entity_type in seed_specs: | |
| table_columns = set(schemas.empty(table).columns) | |
| if yaml_file == "__merged_models__": | |
| items = _load_models_merged() | |
| if not items: | |
| typer.echo(f" [skip] no model entries found in seed/models.yaml or _overrides/") | |
| continue | |
| elif yaml_file == "__merged_orgs__": | |
| items = _load_orgs_merged() | |
| if not items: | |
| typer.echo(f" [skip] no org entries found in seed/orgs.yaml or seed/orgs.generated.yaml") | |
| continue | |
| elif yaml_file == "__merged_benchmarks__": | |
| items = _load_benchmarks_merged() | |
| if not items: | |
| typer.echo(f" [skip] no benchmark entries found in seed/benchmarks.yaml or seed/benchmarks_generated/") | |
| continue | |
| elif yaml_file == "__nested_families__": | |
| items = _load_families_seed() | |
| if not items: | |
| typer.echo(f" [skip] no family entries found in seed/families.yaml") | |
| continue | |
| elif yaml_file == "__nested_composites__": | |
| items = _load_composites_seed() | |
| if not items: | |
| typer.echo(f" [skip] no composite entries found in seed/composites.yaml") | |
| continue | |
| else: | |
| if not yaml_file.exists(): | |
| typer.echo(f" [skip] {yaml_file} not found") | |
| continue | |
| with open(yaml_file) as f: | |
| items = yaml.safe_load(f) or [] | |
| yaml_ids: set[str] = set() | |
| yaml_alias_keys: set[tuple[str, str, str, Optional[str]]] = set() | |
| # Anti-shadowing (models): a community/non-lab model's display_name is a | |
| # noisy identity signal. When a fork's bare display_name (e.g. | |
| # "aya-expanse-8b") normalizes to the same form as a real LAB model | |
| # ("Aya Expanse 8B"), auto-seeding it as a global alias makes the fork | |
| # WIN the bare name at confidence 1.0 and shadow the lab. Build the set | |
| # of lab-owned normalized forms so we can drop only the COLLIDING | |
| # display_name aliases off non-lab models (non-colliding ones still seed, | |
| # so coverage is unaffected). org kind comes from canonical_orgs, which | |
| # seeds before models. | |
| lab_norms: set[str] = set() | |
| # Prefer-official: normalized bare LEAF -> owning canonical id, decided | |
| # by precedence instead of seed load order: | |
| # 1. lab_leaf_owner — a LAB model owns its leaf. A lab rarely seeds | |
| # its own bare leaf as an alias (its forms are org-prefixed), so a | |
| # non-lab FORK sharing the leaf (jebish7/Llama-3.1-8B-Instruct, | |
| # whose display IS the bare "Llama-3.1-8B-Instruct") would | |
| # otherwise win the un-namespaced name. | |
| # 2. strong_leaf_owner — else the leaf's UNIQUE confirmed entry | |
| # (hf-sourced or reviewed) owns it: a machine-minted draft | |
| # (models.dev / tier3 name inference, where org mis-attributions | |
| # live) never beats a real repo (e.g. the models.dev draft | |
| # `alibaba/dolphin-2-9.2-qwen2-72b` vs the real hf | |
| # `dphn/dolphin-2.9.2-qwen2-72b`). Applied only when the alias's | |
| # own entry is neither confirmed nor lab-owned — evidence can rank | |
| # a draft below a real repo, but not two real repos against each | |
| # other, and a lab draft (e.g. `microsoft/WizardLM-2-8x22B`, repo | |
| # deleted upstream) keeps its name on developer-attribution | |
| # grounds. | |
| # Leaves with 2+ confirmed non-lab claimants stay unowned: those are | |
| # duplicate re-uploads to deduplicate, not a name-preference call. | |
| lab_leaf_owner: dict[str, str] = {} | |
| lab_owner_dev: dict[str, str] = {} | |
| strong_leaf_owner: dict[str, str] = {} | |
| strong_ids: set[str] = set() | |
| org_kind: dict[str, str] = {} | |
| hf_to_dev: dict[str, str] = {} | |
| org_names: dict[str, str] = {} | |
| def _entry_dev(it: dict) -> str: | |
| # Resolved developer org id for a seed entry. A set org_id is | |
| # authoritative (curated remaps put the TRUE developer there — | |
| # e.g. the mis-prefixed draft `alibaba/dolphin-2-9.2-qwen2-72b` | |
| # carries org_id `cognitivecomputations`, and trusting its | |
| # `alibaba/` prefix would launder the mis-attribution into lab | |
| # status). Only when org_id is unset at load time (derived later, | |
| # e.g. dated snapshots) fall back to the id's org prefix, resolved | |
| # through the curated org map and then the merged-orgs name map — | |
| # the same surface forms `derive_model_lineage_fields` uses to | |
| # fill org_id, so the seed's lab decision and the seeded org_id | |
| # agree (a prefix-shaped org display_name like "Scale" resolves | |
| # identically in both). | |
| org_id = it.get("org_id") | |
| if org_id: | |
| return str(org_id) | |
| cid = str(it.get("id") or "") | |
| if "/" not in cid: | |
| return "" | |
| prefix = cid.split("/", 1)[0] | |
| return hf_to_dev.get(prefix.lower()) or org_names.get(prefix.lower()) or prefix | |
| def _entry_is_lab(it: dict) -> bool: | |
| return org_kind.get(_entry_dev(it)) == "lab" | |
| if entity_type == "model": | |
| # Read kinds from the YAML source, not store.table — orgs above were | |
| # upserted buffered=True and aren't flushed yet. Curated labs live in | |
| # orgs.yaml; any org absent here (or kind!=lab) is treated as non-lab. | |
| merged_orgs = [ | |
| o for o in (_load_orgs_merged() or []) if isinstance(o, dict) and o.get("id") | |
| ] | |
| org_kind = {str(o["id"]): str(o.get("kind") or "") for o in merged_orgs} | |
| hf_to_dev = build_hf_to_dev_from_orgs_yaml(seed_path / "orgs.yaml") | |
| for o in merged_orgs: | |
| oid = str(o["id"]) | |
| for name in [oid, o.get("hf_org"), o.get("display_name"), *(o.get("aliases") or [])]: | |
| if isinstance(name, str) and name: | |
| org_names.setdefault(name.lower(), oid) | |
| strong_claimants: dict[str, set[str]] = {} | |
| for it in items: | |
| if not isinstance(it, dict): | |
| continue | |
| cid = str(it.get("id") or "") | |
| # "Confirmed" = a real repo (hf-sourced) or a reviewed entry — | |
| # never a tier3 name-inferred draft, which is where org | |
| # mis-attributions live (e.g. `meta/reflection-llama-3.1-70b`, | |
| # inferred, would otherwise steal `Reflection-Llama-3.1-70B` | |
| # from the real hf `mattshumer/Reflection-Llama-3.1-70B`). | |
| confirmed = "/" in cid and ( | |
| it.get("resolution_source") == "hf" | |
| or it.get("review_status") == "reviewed" | |
| ) | |
| if confirmed: | |
| strong_ids.add(cid) | |
| strong_claimants.setdefault( | |
| _normalize_alias(cid.split("/", 1)[1]), set() | |
| ).add(cid) | |
| if not _entry_is_lab(it): | |
| continue | |
| # Only the aliases a lab actually SEEDS (display_name + explicit | |
| # aliases) can catch a raw via normalized match — so only drop a | |
| # community display that collides with one of THOSE. (name-part / | |
| # id are NOT bare aliases, so dropping on them would orphan the raw.) | |
| for s in [it.get("display_name"), *(it.get("aliases") or [])]: | |
| if isinstance(s, str) and s: | |
| lab_norms.add(_normalize_alias(s)) | |
| if confirmed: | |
| # First claimant wins on a shared lab leaf; the gate | |
| # (test_lab_leaf_ownership_is_unambiguous) enforces that | |
| # no two confirmed lab models actually share one. | |
| _leaf_norm = _normalize_alias(cid.split("/", 1)[1]) | |
| if lab_leaf_owner.setdefault(_leaf_norm, cid) == cid: | |
| lab_owner_dev[_leaf_norm] = _entry_dev(it) | |
| strong_leaf_owner = { | |
| norm: next(iter(cids)) | |
| for norm, cids in strong_claimants.items() | |
| if len(cids) == 1 | |
| } | |
| for original_item in items: | |
| item = dict(original_item) | |
| # Pop 'aliases' / 'scoped_aliases' before upserting — not table columns. | |
| extra_aliases = item.pop("aliases", []) or [] | |
| scoped_aliases = item.pop("scoped_aliases", {}) or {} | |
| # Normalize list/dict columns: YAML may have native lists/dicts, | |
| # but the canonical_* parquet columns are VARCHAR, so encode if | |
| # needed. `parents` is a list-of-edges on canonical_models. | |
| # `benchmark_ids` / `folder_aliases` / `composite_keys` are | |
| # list-valued on canonical_families. `source_configs` is | |
| # list-valued on canonical_composites. | |
| for col in ( | |
| "tags", "metadata", "parents", | |
| "input_modalities", "output_modalities", | |
| "benchmark_ids", "folder_aliases", "composite_keys", | |
| "source_configs", | |
| ): | |
| if col in item: | |
| item[col] = _json_encode_if_needed(item[col]) | |
| entity_item = {k: v for k, v in item.items() if k in table_columns} | |
| unknown_keys = sorted(set(item.keys()) - table_columns) | |
| if unknown_keys: | |
| typer.echo( | |
| f" [warn] {label} entry {item.get('id', '?')!r} has unknown " | |
| f"key(s) {unknown_keys} — silently dropped. Check for typos." | |
| ) | |
| if "id" not in entity_item: | |
| raise typer.BadParameter(f"{label} seed entry is missing required id: {original_item!r}") | |
| # Presentation-only: humanize the STORED display column for label-less | |
| # model rows (tier-3 raws, empty, id-placeholder). Alias promotion | |
| # below uses `display_name` = the ORIGINAL value, so resolution is | |
| # unchanged — display coverage is decoupled from resolution. | |
| display_name = entity_item.get("display_name", "") | |
| if entity_type == "model": | |
| humanized = _humanized_display(entity_item) | |
| if humanized: | |
| entity_item["display_name"] = humanized | |
| queries.upsert_entity(store, table, entity_item, buffered=True) | |
| canonical_id = entity_item["id"] | |
| yaml_ids.add(canonical_id) | |
| # Global aliases (source_config=None): matched regardless of caller's source_config. | |
| # Scoped aliases (source_config=<name>): matched only when the caller passes that | |
| # source_config — lets short tokens ("Overall", "Arabic") map to different | |
| # benchmarks depending on which EEE config they came from. | |
| global_aliases = {canonical_id, display_name} | set(extra_aliases) | |
| # Drop a non-lab model's display_name alias when it would shadow a | |
| # lab model (see lab_norms above). canonical_id + explicit aliases | |
| # are kept — only the auto-promoted display_name is suppressed. | |
| # global_aliases is a set, so when display_name IS the canonical_id | |
| # (id-placeholder display) or an explicitly curated alias, | |
| # discarding it would destroy that stronger claim too (an entity | |
| # unresolvable by its own id) — never drop those strings. | |
| item_dev = _entry_dev(entity_item) if entity_type == "model" else "" | |
| item_is_lab = org_kind.get(item_dev) == "lab" | |
| if ( | |
| entity_type == "model" | |
| and display_name | |
| and display_name != canonical_id | |
| and display_name not in extra_aliases | |
| and not item_is_lab | |
| and _normalize_alias(display_name) in lab_norms | |
| ): | |
| global_aliases.discard(display_name) | |
| alias_specs: list[tuple[str, Optional[str]]] = [ | |
| (raw, None) for raw in global_aliases if raw | |
| ] | |
| for source_cfg, raw_values in scoped_aliases.items(): | |
| if not isinstance(source_cfg, str) or not _SOURCE_SCOPE_KEY_RE.match(source_cfg): | |
| raise typer.BadParameter( | |
| f"{label} entry {canonical_id!r}: scoped_aliases key " | |
| f"{source_cfg!r} is not a valid EEE dataset config name" | |
| ) | |
| source_scope_keys.add(source_cfg) | |
| for raw in raw_values or []: | |
| if raw: | |
| alias_specs.append((raw, source_cfg)) | |
| for raw_value, source_cfg in alias_specs: | |
| # Prefer-official redirect: an un-namespaced leaf alias (a fork's | |
| # bare display "Llama-3.1-8B-Instruct", or a short/dated form) is | |
| # pointed at the leaf's owner (see the precedence above | |
| # lab_leaf_owner / strong_leaf_owner) so the official model wins | |
| # the bare name, not whichever entry sorts first. Global aliases | |
| # only (source_config=None). The lab tier takes names from | |
| # non-lab entries and from SAME-developer lab entries (a lab's | |
| # models.dev/casing mint folding onto its confirmed repo) but | |
| # never across distinct labs — a lab entry keeps its name on | |
| # developer-attribution grounds even against another lab's | |
| # colliding leaf. The confirmed tier only takes names FROM | |
| # unconfirmed non-lab drafts. | |
| target_cid = canonical_id | |
| if entity_type == "model" and source_cfg is None: | |
| _norm = _normalize_alias(raw_value) | |
| _lab = lab_leaf_owner.get(_norm) | |
| if _lab and _lab != canonical_id and ( | |
| not item_is_lab or lab_owner_dev.get(_norm) == item_dev | |
| ): | |
| target_cid = _lab | |
| elif not item_is_lab and canonical_id not in strong_ids: | |
| _strong = strong_leaf_owner.get(_norm) | |
| if _strong and _strong != canonical_id: | |
| target_cid = _strong | |
| # Index stale-removal by (raw_value, entity_type, canonical_id, source_config) | |
| yaml_alias_keys.add((raw_value, entity_type, target_cid, source_cfg)) | |
| # Same-run collision check (see run_alias_owners above): if a | |
| # different canonical already claimed this exact alias in this | |
| # pass, the owner would be nondeterministic — record it. | |
| _claim_key = (raw_value, entity_type, queries._source_config_key(source_cfg)) | |
| _prev = run_alias_owners.get(_claim_key) | |
| if _prev is None: | |
| run_alias_owners[_claim_key] = (target_cid, canonical_id) | |
| elif _prev[0] != target_cid: | |
| alias_collisions.append( | |
| (raw_value, entity_type, _claim_key[2], | |
| _prev[0], target_cid, _prev[1], canonical_id) | |
| ) | |
| try: | |
| queries.add_alias(store, { | |
| "raw_value": raw_value, | |
| "entity_type": entity_type, | |
| "canonical_id": target_cid, | |
| "source_config": source_cfg, | |
| "source_field": "seed", | |
| "status": "confirmed", | |
| "strategy": "seed", | |
| "confidence": 1.0, | |
| "notes": None, | |
| }, buffered=True) | |
| alias_count += 1 | |
| except ValueError: | |
| # add_alias raises on uniqueness collision: an alias row | |
| # already exists for (entity_type, raw_value, source_config). | |
| # YAML is the source of truth, so if the existing row points | |
| # at a different canonical_id, this is a YAML rename and we | |
| # must REPOINT the existing row — NOT silently swallow it. | |
| # Without this, stale-removal at the end of seed would then | |
| # delete the row (its old key is no longer in | |
| # yaml_alias_keys), causing total alias loss. | |
| aliases_df = store.table("aliases") | |
| mask = ( | |
| (aliases_df["raw_value"] == raw_value) | |
| & (aliases_df["entity_type"] == entity_type) | |
| & (aliases_df["status"] != "rejected") | |
| ) | |
| if source_cfg is not None: | |
| mask = mask & (aliases_df["source_config"] == source_cfg) | |
| else: | |
| mask = mask & aliases_df["source_config"].isna() | |
| existing = aliases_df[mask] | |
| if existing.empty: | |
| # Collision came from the pending buffer (this run added | |
| # the same key earlier). For same-canonical re-adds this | |
| # is a no-op; for different-canonical we must mutate the | |
| # pending dict in place so the rename isn't lost on | |
| # flush. _alias_index points at the same dict, so | |
| # updating it here keeps the index consistent. | |
| # Repoint to target_cid, NOT canonical_id: a redirected | |
| # alias (see target_cid above) must keep its redirect | |
| # target even when a duplicate same-run claim lands here | |
| # — repointing to the raw claimant would silently undo | |
| # prefer-official for exactly the contested names it | |
| # exists to protect. | |
| for p in queries._get_pending(store, "aliases"): | |
| if (p.get("entity_type") == entity_type | |
| and p.get("raw_value") == raw_value | |
| and queries._source_config_key(p.get("source_config")) == queries._source_config_key(source_cfg) | |
| and p.get("status") != "rejected"): | |
| if p["canonical_id"] != target_cid: | |
| prev = p["canonical_id"] | |
| p["canonical_id"] = target_cid | |
| p["source_field"] = "seed" | |
| p["status"] = "confirmed" | |
| p["strategy"] = "seed" | |
| p["confidence"] = 1.0 | |
| typer.echo( | |
| f" [rename] alias {raw_value!r} ({entity_type}) " | |
| f"moved {prev!r} -> {target_cid!r} (pending)" | |
| ) | |
| alias_count += 1 | |
| break | |
| continue | |
| row = existing.iloc[0] | |
| if row["canonical_id"] != target_cid: | |
| # Rename: repoint the existing row at the new canonical. | |
| queries.update_alias(store, row["id"], { | |
| "canonical_id": target_cid, | |
| "source_field": "seed", | |
| "status": "confirmed", | |
| "strategy": "seed", | |
| "confidence": 1.0, | |
| }) | |
| typer.echo( | |
| f" [rename] alias {raw_value!r} ({entity_type}) " | |
| f"moved {row['canonical_id']!r} -> {target_cid!r}" | |
| ) | |
| alias_count += 1 | |
| # else: identical re-seed of an existing alias — no-op. | |
| seed_snapshot.append((table, entity_type, yaml_ids, yaml_alias_keys)) | |
| typer.echo(f" {label}: {len(items)}") | |
| # Fail fast on same-run alias collisions (nondeterministic owner). Don't | |
| # flush — dirty data must not persist. Each line names the contended alias | |
| # and the two canonicals; fix by removing the duplicate declaration from | |
| # the non-owning entity (per project rule: the later/more-specific entity | |
| # owns the name). | |
| if alias_collisions: | |
| deduped = sorted(set(alias_collisions)) | |
| lines = "\n".join( | |
| f" {rv!r} ({et}{', cfg=' + ck if ck else ''}): " | |
| f"would point at both {a!r} (declared by {da!r}) " | |
| f"and {b!r} (declared by {db!r})" | |
| for (rv, et, ck, a, b, da, db) in deduped | |
| ) | |
| raise typer.BadParameter( | |
| f"{len(deduped)} alias collision(s) — the same alias is declared by " | |
| "more than one canonical in this seed pass, so the owner would be " | |
| "seed-order-dependent (nondeterministic). Each alias must belong to " | |
| "exactly one canonical:\n" + lines | |
| ) | |
| # Flush all buffered upserts (entities + aliases) into their tables in a | |
| # single pd.concat per table. prune_stale below reads store.table(...) | |
| # directly, so this must happen before that block. | |
| queries.flush_pending(store) | |
| # ------------------------------------------------------------------ | |
| # Inference platforms — flat dim table. Loaded directly into | |
| # canonical_inference_platforms rather than through the seed_specs loop: | |
| # its `aliases` column is a stored JSON list of host-token spellings (the | |
| # single source for lib/inference_platforms_map.py), NOT alias-table rows. | |
| # ------------------------------------------------------------------ | |
| def _load_inference_platforms_merged() -> list[dict]: | |
| path = seed_path / "inference_platforms.yaml" | |
| if not path.exists(): | |
| return [] | |
| with open(path) as f: | |
| loaded = yaml.safe_load(f) or [] | |
| if not isinstance(loaded, list): | |
| raise typer.BadParameter(f"{path} must be a flat list") | |
| return loaded | |
| inf_plat_entries = _load_inference_platforms_merged() | |
| if inf_plat_entries: | |
| import pandas as pd | |
| now = queries._now() | |
| inf_cols = list(schemas._SCHEMAS["canonical_inference_platforms"].keys()) | |
| inf_plat_rows = [] | |
| for e in inf_plat_entries: | |
| if "id" not in e or "display_name" not in e or "kind" not in e: | |
| raise typer.BadParameter( | |
| f"inference_platforms entry missing required field: {e!r}" | |
| ) | |
| row = { | |
| "id": e.get("id"), | |
| "display_name": e.get("display_name"), | |
| "kind": e.get("kind"), | |
| "aliases": _json_encode_if_needed(e.get("aliases")), | |
| "canonical_org": e.get("canonical_org"), | |
| "variant_of": e.get("variant_of"), | |
| "homepage": e.get("homepage"), | |
| "created_at": now, | |
| "updated_at": now, | |
| } | |
| inf_plat_rows.append(row) | |
| inf_plat_df = pd.DataFrame(inf_plat_rows) | |
| # Schema-pad any missing columns and order to match the schema. | |
| for col in inf_cols: | |
| if col not in inf_plat_df.columns: | |
| inf_plat_df[col] = None | |
| inf_plat_df = inf_plat_df[inf_cols] | |
| store.set_table("canonical_inference_platforms", inf_plat_df) | |
| typer.echo(f" inference_platforms: {len(inf_plat_rows)}") | |
| # ------------------------------------------------------------------ | |
| # Benchmark metric folds — flat dim table (no entity ids, so it skips | |
| # the seed_specs/upsert path). Entries state that `from_metric` on | |
| # `benchmark` is the preferred measurement under a generic name. | |
| # ------------------------------------------------------------------ | |
| folds_path = seed_path / "metric_folds.yaml" | |
| if folds_path.exists(): | |
| import pandas as pd | |
| with open(folds_path) as f: | |
| fold_entries = yaml.safe_load(f) or [] | |
| if not isinstance(fold_entries, list): | |
| raise typer.BadParameter(f"{folds_path} must be a flat list") | |
| bench_ids = {e["id"] for e in _load_benchmarks_merged()} | |
| fold_metrics_path = seed_path / "metrics.yaml" | |
| metrics_by_id = {} | |
| if fold_metrics_path.exists(): | |
| with open(fold_metrics_path) as f: | |
| metrics_by_id = {m["id"]: m for m in (yaml.safe_load(f) or [])} | |
| metric_ids = set(metrics_by_id) | |
| seen_rows: set[tuple[str, str, Optional[str]]] = set() | |
| unscoped_pairs: dict[tuple[str, str], str] = {} | |
| fold_rows = [] | |
| for e in fold_entries: | |
| b, fm, tm = e.get("benchmark"), e.get("from_metric"), e.get("to_metric") | |
| source_config = e.get("source_config") | |
| if not (b and fm and tm): | |
| raise typer.BadParameter(f"metric_folds entry needs benchmark/from_metric/to_metric: {e!r}") | |
| if b not in bench_ids: | |
| raise typer.BadParameter(f"metric_folds: unknown benchmark {b!r}") | |
| missing = [m for m in (fm, tm) if m not in metric_ids] | |
| if missing: | |
| raise typer.BadParameter(f"metric_folds ({b}): unknown metric id(s) {missing}") | |
| if source_config is not None and not isinstance(source_config, str): | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): source_config must be a string or null" | |
| ) | |
| if source_config is not None and not _SOURCE_SCOPE_KEY_RE.match(source_config): | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): source_config {source_config!r} is not a " | |
| "valid EEE dataset config name" | |
| ) | |
| if source_config is not None: | |
| source_scope_keys.add(source_config) | |
| # A scoped row may fold a metric onto ITSELF: that is a pure | |
| # published-scale conversion for one source, not a rename. An | |
| # unscoped row saying `x is x` carries no information. | |
| if fm == tm and source_config is None: | |
| raise typer.BadParameter(f"metric_folds ({b}): self-fold {fm!r}") | |
| row_key = (b, fm, source_config) | |
| if row_key in seen_rows: | |
| raise typer.BadParameter( | |
| f"metric_folds: duplicate fold for ({b}, {fm}, {source_config!r})" | |
| ) | |
| seen_rows.add(row_key) | |
| if source_config is None: | |
| unscoped_pairs[(b, fm)] = tm | |
| factor = e.get("scale_factor") | |
| offset = e.get("scale_offset") | |
| if source_config is None and (factor is not None or offset is not None): | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): scale_factor/scale_offset conversions require source_config" | |
| ) | |
| for name, value in (("scale_factor", factor), ("scale_offset", offset)): | |
| if value is not None and ( | |
| not isinstance(value, Real) | |
| or isinstance(value, bool) | |
| or not math.isfinite(value) | |
| ): | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): {name} must be a finite real number, got {value!r}" | |
| ) | |
| if factor is not None and factor <= 0: | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): scale_factor must be a positive number, got {factor!r}" | |
| ) | |
| if factor is not None or offset is not None: | |
| target = metrics_by_id[tm] | |
| min_score, max_score = target.get("min_score"), target.get("max_score") | |
| if ( | |
| not isinstance(min_score, Real) | |
| or isinstance(min_score, bool) | |
| or not math.isfinite(min_score) | |
| or not isinstance(max_score, Real) | |
| or isinstance(max_score, bool) | |
| or not math.isfinite(max_score) | |
| or max_score <= min_score | |
| ): | |
| raise typer.BadParameter( | |
| f"metric_folds ({b}): {tm!r} must have finite ordered numeric bounds " | |
| "when scale_factor or scale_offset is set" | |
| ) | |
| fold_rows.append({ | |
| "benchmark_id": b, "from_metric_id": fm, | |
| "to_metric_id": tm, "source_config": source_config, | |
| "scale_factor": factor, "scale_offset": offset, | |
| "note": e.get("note"), | |
| }) | |
| # A scoped row is either (a) the per-source conversion attached to a | |
| # benchmark-wide naming fold — then it must name that fold's target — | |
| # or (b) a standalone published-scale conversion, which renames | |
| # nothing (from_metric == to_metric) and must actually carry a | |
| # conversion. Naming a DIFFERENT target than the benchmark-wide row | |
| # would make one source's metric mean something else, so it is | |
| # rejected either way. | |
| for row in fold_rows: | |
| source_config = row["source_config"] | |
| if source_config is None: | |
| continue | |
| pair = (row["benchmark_id"], row["from_metric_id"]) | |
| unscoped_target = unscoped_pairs.get(pair) | |
| if unscoped_target is not None: | |
| if row["to_metric_id"] != unscoped_target: | |
| raise typer.BadParameter( | |
| f"metric_folds ({pair[0]}): scoped to_metric must match unscoped row" | |
| ) | |
| elif not ( | |
| row["to_metric_id"] == row["from_metric_id"] | |
| and (row["scale_factor"] is not None or row["scale_offset"] is not None) | |
| ): | |
| raise typer.BadParameter( | |
| f"metric_folds ({pair[0]}): scoped row requires unscoped row for " | |
| f"{pair[1]!r}, unless it is a pure published-scale conversion " | |
| "(from_metric == to_metric with scale_factor/scale_offset)" | |
| ) | |
| # No chains: a fold target must not itself be folded on the same benchmark. | |
| targets = {(r["benchmark_id"], r["to_metric_id"]) for r in fold_rows} | |
| chained = sorted(targets & set(unscoped_pairs)) | |
| if chained: | |
| raise typer.BadParameter(f"metric_folds: chained folds {chained}") | |
| folds_df = pd.DataFrame( | |
| fold_rows, columns=list(schemas._SCHEMAS["benchmark_metric_folds"].keys()) | |
| ) | |
| store.set_table("benchmark_metric_folds", folds_df) | |
| typer.echo(f" metric_folds: {len(fold_rows)}") | |
| # Derive denormalized parent-walk caches now that all canonical_models | |
| # rows are present. `model_group_id` and `lineage_origin_model_org_id` | |
| # are computed from `parents` and need the full graph to be in place. | |
| lineage_counts = queries.derive_model_lineage_fields(store) | |
| typer.echo( | |
| f" derived: model_group_id={lineage_counts['group_set']}, " | |
| f"model_family_id={lineage_counts['family_set']}, " | |
| f"lineage_origin_model_id={lineage_counts['lineage_model_set']}, " | |
| f"lineage_origin_model_org_id={lineage_counts['lineage_org_set']}, " | |
| f"open_weights_inherited={lineage_counts['open_weights_inherited']}, " | |
| f"release_date_from_id={lineage_counts['release_date_derived_from_id']}" | |
| ) | |
| removed_entities = 0 | |
| removed_aliases = 0 | |
| if prune_stale: | |
| # Remove seed-originated entities and aliases that are no longer in the YAML. | |
| # Only touches rows that were created by seed (strategy == "seed"), never | |
| # sync-created aliases or auto-draft entities. | |
| for table, entity_type, yaml_ids, yaml_alias_keys in seed_snapshot: | |
| # Remove stale seed aliases for this entity type. | |
| aliases_df = store.table("aliases") | |
| seed_mask = (aliases_df["strategy"] == "seed") & (aliases_df["entity_type"] == entity_type) | |
| if seed_mask.any(): | |
| seed_aliases = aliases_df[seed_mask] | |
| stale_alias_mask = seed_mask.copy() | |
| for idx in seed_aliases.index: | |
| row = seed_aliases.loc[idx] | |
| sc = row.get("source_config") | |
| if _is_na(sc): | |
| sc = None | |
| key = (row["raw_value"], row["entity_type"], row["canonical_id"], sc) | |
| if key in yaml_alias_keys: | |
| stale_alias_mask[idx] = False | |
| n_stale = stale_alias_mask.sum() | |
| if n_stale > 0: | |
| store.set_table("aliases", aliases_df[~stale_alias_mask].reset_index(drop=True)) | |
| removed_aliases += int(n_stale) | |
| # Remove stale seed entities — only those with review_status "reviewed" | |
| # that came from seed and are no longer in the YAML. | |
| entity_df = store.table(table) | |
| if len(entity_df) > 0: | |
| stale = entity_df["id"].isin(yaml_ids) | |
| stale_entities = entity_df[~stale & (entity_df["review_status"] == "reviewed")] | |
| # FK guard: never prune an org still referenced by a surviving | |
| # FK. A model whose org_id is DERIVED from its id-prefix (no | |
| # curated remap) gets an org row auto-created at seed time that | |
| # isn't in the orgs YAML; without this guard prune would drop it | |
| # and orphan the model (dangling org_id FK). Orgs also reference | |
| # other orgs via parent_org_id, so an org that is any other org's | |
| # parent must be kept too. | |
| if entity_type == "org" and len(stale_entities) > 0: | |
| referenced: set[str] = set() | |
| models = store.table("canonical_models") | |
| if models is not None and len(models) > 0: | |
| for col in ("org_id", "lineage_origin_model_org_id"): | |
| if col in models.columns: | |
| referenced |= { | |
| str(x) for x in models[col].dropna().astype(str) | |
| } | |
| if "parent_org_id" in entity_df.columns: | |
| referenced |= { | |
| str(x) for x in entity_df["parent_org_id"].dropna().astype(str) | |
| } | |
| stale_entities = stale_entities[ | |
| ~stale_entities["id"].astype(str).isin(referenced) | |
| ] | |
| # Only remove if every alias for this entity is also seed-originated, | |
| # meaning it wasn't referenced by sync data. | |
| current_aliases = store.table("aliases") | |
| for eid in stale_entities["id"]: | |
| entity_aliases = current_aliases[ | |
| (current_aliases["canonical_id"] == eid) | |
| & (current_aliases["entity_type"] == entity_type) | |
| ] | |
| if len(entity_aliases) == 0 or (entity_aliases["strategy"] == "seed").all(): | |
| entity_df = entity_df[entity_df["id"] != eid] | |
| # Also remove any remaining aliases pointing to it. | |
| current_aliases = current_aliases[ | |
| ~((current_aliases["canonical_id"] == eid) | |
| & (current_aliases["entity_type"] == entity_type)) | |
| ] | |
| removed_entities += 1 | |
| store.set_table(table, entity_df.reset_index(drop=True)) | |
| store.set_table("aliases", current_aliases.reset_index(drop=True)) | |
| typer.echo(f" aliases: {alias_count} added, {removed_aliases} removed") | |
| typer.echo( | |
| " source scope keys: " | |
| + (", ".join(sorted(source_scope_keys)) if source_scope_keys else "(none)") | |
| ) | |
| if removed_entities: | |
| typer.echo(f" stale entities removed: {removed_entities}") | |
| store.push_to_hub() | |
| typer.echo("Seed complete.") | |
| # ------------------------------------------------------------------ | |
| # stats | |
| # ------------------------------------------------------------------ | |
| def stats( | |
| local: bool = typer.Option(False, "--local", help="Read from fixtures/ instead of HF Hub"), | |
| ): | |
| """Print registry entity counts and pending review summary.""" | |
| import os | |
| if local: | |
| os.environ["LOCAL_MODE"] = "true" | |
| store = _load_store() | |
| def _row(table): | |
| df = store.table(table) | |
| total = len(df) | |
| draft = int((df["review_status"] == "draft").sum()) if "review_status" in df.columns else 0 | |
| return total, draft | |
| for label, table in [ | |
| ("models ", "canonical_models"), | |
| ("benchmarks", "canonical_benchmarks"), | |
| ("metrics ", "canonical_metrics"), | |
| ("harnesses ", "eval_harnesses"), | |
| ]: | |
| total, draft = _row(table) | |
| typer.echo(f" {label} total={total} draft={draft}") | |
| aliases_df = store.table("aliases") | |
| uncertain = int((aliases_df["status"] == "uncertain").sum()) if "status" in aliases_df.columns else 0 | |
| typer.echo(f"\n aliases total={len(aliases_df)} uncertain={uncertain}") | |
| typer.echo(f" eval_results total={len(store.table('eval_results'))}") | |
| typer.echo(f" resolution_log total={len(store.table('resolution_log'))}") | |
| typer.echo(f" sync_runs total={len(store.table('sync_runs'))}") | |
| # ------------------------------------------------------------------ | |
| # sync | |
| # ------------------------------------------------------------------ | |
| def sync( | |
| config: Optional[str] = typer.Option(None, "--config", help="EEE config name"), | |
| all_configs: bool = typer.Option(False, "--all", help="Sync all EEE configs"), | |
| rerun: bool = typer.Option(False, "--rerun", help="Re-resolve all raw strings even if already aliased"), | |
| local: bool = typer.Option(False, "--local"), | |
| ): | |
| """ | |
| Batch sync EEE config(s) → writes resolved results to eval_results table. | |
| Each result row is one (model × benchmark × metric) combination with resolved canonical IDs. | |
| """ | |
| import os | |
| if local: | |
| os.environ["LOCAL_MODE"] = "true" | |
| if not config and not all_configs: | |
| typer.echo("Specify --config <name> or --all", err=True) | |
| raise typer.Exit(1) | |
| from eval_entity_registry.services.ingestion import run_sync | |
| import datasets as ds_lib | |
| store = _load_store() | |
| configs_to_run: list[str] = [] | |
| if all_configs: | |
| configs_to_run = ds_lib.get_dataset_config_names("evaleval/EEE_datastore") | |
| else: | |
| configs_to_run = [config] | |
| failed = [] | |
| for cfg in configs_to_run: | |
| typer.echo(f"Syncing {cfg}...") | |
| try: | |
| counts = run_sync(cfg, store, rerun=rerun) | |
| typer.echo(f" {cfg}: {counts}") | |
| except Exception as e: | |
| typer.echo(f" {cfg}: FAILED — {e}", err=True) | |
| failed.append(cfg) | |
| typer.echo("Persisting tables...") | |
| store.push_to_hub() | |
| if failed: | |
| typer.echo(f"Done with {len(failed)} failed config(s): {', '.join(failed)}") | |
| else: | |
| typer.echo("Done.") | |