1"""Model identity ontology — the rules that separate a MODEL (release) from its ARTIFACTS (checkpoints, conversions, quantisations),2its EVALUATION VARIANTS (reasoning-effort settings of the same weights) and its FAMILY.34 MODEL FAMILY Llama 4 · Qwen3.6 · Claude · Gemini5 └ MODEL Llama 4 Maverick · Qwen3.6-35B-A3B · Claude Fable 5.16 └ ARTIFACT meta-llama/Llama-4-Maverick-17B-128E-Instruct (official checkpoint) · unsloth/…-GGUF (third-party quantisation)7 └ DEPLOYMENT OpenRouter meta-llama/llama-4-maverick · Together meta-llama/Llama-4-Maverick (= prices rows)89Everything here is deterministic string analysis. It never *asserts* a relation on its own: the resolution service uses these10signals together with identifiers, organisations, `base_model` metadata and dates, and parks anything ambiguous in the review queue.11"""12from __future__ import annotations1314import re15from dataclasses import dataclass, field1617# ---------------------------------------------------------------------------------------------- quantisation / precision formats18QUANT_FORMATS: dict[str, str] = {19 # token (lowercase, matched on word boundaries) → canonical format20 "gguf": "gguf", "ggml": "gguf", "q2_k": "gguf", "q3_k_m": "gguf", "q4_k_m": "gguf", "q4_k_s": "gguf", "q5_k_m": "gguf", "q6_k": "gguf", "q8_0": "gguf",21 "q4_0": "gguf", "q4_1": "gguf", "q5_0": "gguf", "iq4_xs": "gguf", "iq3_m": "gguf", "iq2_m": "gguf", "ud-q4_k_xl": "gguf",22 "awq": "awq", "w4a16": "awq", "gptq": "gptq", "exl2": "exl2", "exl3": "exl3", "mlx": "mlx", "onnx": "onnx", "openvino": "openvino", "coreml": "coreml",23 "tensorrt": "tensorrt", "trt": "tensorrt", "trt-llm": "tensorrt", "bnb": "bnb", "bitsandbytes": "bnb", "nf4": "bnb", "4bit": "int4", "8bit": "int8",24 "int4": "int4", "int8": "int8", "w8a8": "int8", "w8a16": "int8", "fp8": "fp8", "fp8-kv": "fp8", "e4m3": "fp8", "nvfp4": "nvfp4", "mxfp4": "mxfp4",25 "mxfp8": "mxfp8", "fp4": "fp4", "quark": "quark", "quantized": "quantized", "quant": "quantized", "qat": "qat", "hqq": "hqq", "aqlm": "aqlm", "eetq": "eetq",26 "smoothquant": "smoothquant", "compressed-tensors": "compressed-tensors", "marlin": "gptq", "gptq-int4": "gptq", "autoround": "autoround",27 "2bit": "int2", "3bit": "int3", "5bit": "int5", "6bit": "int6", "2-bit": "int2", "3-bit": "int3", "4-bit": "int4", "5-bit": "int5", "6-bit": "int6", "8-bit": "int8",28}29# Full-precision dtype tokens: a "-BF16" or "-FP16" repo is a *conversion/packaging* of the same weights, not a quantisation.30PRECISION_FORMATS = {"bf16", "fp16", "fp32", "f16", "f32", "float16", "bfloat16", "half"}31ARTIFACT_PACKAGING = {"safetensors", "pytorch", "pth", "ckpt", "jax", "flax", "tf", "tflite", "litert", "gguf", "mlx", "onnx", "coreml", "openvino", "tensorrt"}3233# Third-party organisations that (almost) only publish conversions/quantisations of other people's models.34CONVERTER_ORGS = {35 "unsloth", "bartowski", "mlx-community", "thebloke", "lmstudio-community", "qwen-community", "mradermacher", "quantfactory", "nousresearch-quant",36 "mistral-community", "turboderp", "casperhansen", "neuralmagic", "redhatai", "amd", "intel", "nvidia-quant", "ggml-org", "second-state",37 "mlx-vision", "cortexso", "gaianet", "bunnycore", "dranger003", "ubergarm", "anthracite-org", "modelcloud", "jinaai-quant", "ai-forever-quant",38 "lmstudio", "ollama", "ggerganov", "mlc-ai", "onnx-community", "onnxmodelzoo", "kaitchup", "thedrummer-quant",39}4041# ---------------------------------------------------------------------------------------------- evaluation-effort variants (same weights, different setting)42# Suffixes appended by evaluators (Artificial Analysis, LiveBench…) to a model slug to denote a *configuration* of the model.43EFFORT_SUFFIXES: dict[str, dict[str, str]] = {44 "xhigh": {"reasoning_effort": "xhigh"}, "x-high": {"reasoning_effort": "xhigh"}, "extra-high": {"reasoning_effort": "xhigh"},45 "high": {"reasoning_effort": "high"}, "medium": {"reasoning_effort": "medium"}, "low": {"reasoning_effort": "low"}, "minimal": {"reasoning_effort": "minimal"},46 "max-effort": {"reasoning_effort": "max"}, "high-effort": {"reasoning_effort": "high"}, "low-effort": {"reasoning_effort": "low"},47 "medium-effort": {"reasoning_effort": "medium"}, "xhigh-effort": {"reasoning_effort": "xhigh"},48 "thinking": {"reasoning": "on"}, "reasoning": {"reasoning": "on"}, "think": {"reasoning": "on"}, "thinking-on": {"reasoning": "on"},49 "non-reasoning": {"reasoning": "off"}, "no-reasoning": {"reasoning": "off"}, "non-thinking": {"reasoning": "off"}, "nothink": {"reasoning": "off"},50 "no-think": {"reasoning": "off"}, "no-thinking": {"reasoning": "off"}, "thinking-off": {"reasoning": "off"}, "without-thinking": {"reasoning": "off"},51 "with-thinking": {"reasoning": "on"},52 "adaptive": {"reasoning": "adaptive"}, "adaptive-reasoning": {"reasoning": "adaptive"},53 # NOT effort suffixes although evaluators sometimes use them: "max" (Qwen3-Max, GPT-5.1-Codex-Max are models), "fast" (Grok 4.1 Fast),54 # "instant" (Claude Instant) — they name a model tier, so they are only recognised in the explicit "-max-effort" form above.55 "thinking-16k": {"reasoning": "on", "thinking_budget": "16k"}, "thinking-32k": {"reasoning": "on", "thinking_budget": "32k"},56 "thinking-64k": {"reasoning": "on", "thinking_budget": "64k"}, "thinking-128k": {"reasoning": "on", "thinking_budget": "128k"},57 "thinking-8k": {"reasoning": "on", "thinking_budget": "8k"}, "thinking-4k": {"reasoning": "on", "thinking_budget": "4k"}, "thinking-1k": {"reasoning": "on", "thinking_budget": "1k"},58}59# order matters: try the longest compound suffixes first60_EFFORT_ORDERED = sorted(EFFORT_SUFFIXES, key=len, reverse=True)61_EFFORT_RE = re.compile(r"[-_ ](" + "|".join(re.escape(s) for s in _EFFORT_ORDERED) + r")$", re.I)62# LiveBench-style budgets: "32k-thinking", "24k-think", "default-think(ing)"63_BUDGET_RE = re.compile(r"[-_ ]((\d+)k)[-_ ]?(thinking|think)$", re.I)64_DEFAULT_THINK_RE = re.compile(r"[-_ ](default[-_ ]?(thinking|think))$", re.I)65_MAX_EFFORT_SUFFIXES = 3 # "claude-opus-4-5-20251101-thinking-64k-high-effort" → thinking-64k + high-effort66# Words that are *part of a model name*, never an effort suffix, when they precede the suffix (e.g. "Kimi K2 Thinking" is a distinct release).67OFFICIAL_THINKING_RELEASES = {"kimi-k2-thinking", "qwen3-235b-a22b-thinking-2507", "qwen3-30b-a3b-thinking-2507", "qwen3-4b-thinking-2507", "glm-4.5-air-thinking",68 "gemini-2.5-flash-thinking", "grok-3-mini-thinking", "gemini-2-0-flash-thinking-exp-1219", "gemini-2-0-flash-thinking-exp-01-21"}6970# ---------------------------------------------------------------------------------------------- name analysis71_SIZE_RE = re.compile(r"(?<![a-z0-9])(\d+(?:\.\d+)?)\s?([bmt])(?![a-z])", re.I) # 70B 3.8B 235B 1.5T 350M72_ACTIVE_RE = re.compile(r"(?<![a-z0-9])(\d+(?:\.\d+)?)\s?[bmt]\s?-?a(\d+(?:\.\d+)?)\s?([bmt])(?![a-z])", re.I) # 35B-A3B73_DATE_RE = re.compile(r"(?<!\d)(20\d{2})[-_.]?(0[1-9]|1[0-2])[-_.]?(0[1-9]|[12]\d|3[01])(?!\d)") # 2024062074_MMDD_RE = re.compile(r"(?<![0-9])(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])(?![0-9])") # 0905 / 0324 (release snapshots)75_YYMM_RE = re.compile(r"(?<![0-9])(2[3-9])(0[1-9]|1[0-2])(?![0-9])") # 2507 (Qwen-style)76_TRAILING_VARIANT_WORDS = {"instruct", "it", "chat", "base", "sft", "dpo", "rl", "hf", "preview", "exp", "experimental", "latest", "beta", "alpha", "v1", "v2", "v3",77 "mtp", "distill", "distilled", "uncensored", "abliterated", "heretic", "merge"}78_TOKEN_SPLIT = re.compile(r"[\s_/]+|(?<=[a-z])(?=[A-Z][a-z])")798081@dataclass82class NameAnalysis:83 raw: str84 repo_org: str | None = None # "unsloth" in "unsloth/Qwen3.6-35B-A3B-GGUF"85 repo_name: str | None = None # "Qwen3.6-35B-A3B-GGUF"86 base_key: str = "" # normalised key with quant/precision/effort tokens removed: "qwen3.6-35b-a3b"87 quant_formats: list[str] = field(default_factory=list) # ["gguf"]88 precision: str | None = None # "bf16" when only a precision tag is present89 is_quantized: bool = False90 is_conversion: bool = False # packaging/precision conversion without quantisation (BF16 repack, MLX fp16, ONNX)91 from_converter_org: bool = False92 effort: dict[str, str] = field(default_factory=dict) # {"reasoning_effort": "xhigh"} when an evaluator suffix was stripped93 effort_suffix: str | None = None94 parameter_count: int | None = None95 active_parameter_count: int | None = None96 snapshot_date: str | None = None # "2024-06-20" / "2025-09" style date embedded in the name97 family_hint: str | None = None # "Qwen3.6", "Llama 4", "Claude", "Gemini 2.5"9899 @property100 def is_artifact(self) -> bool:101 return self.is_quantized or self.is_conversion102103 @property104 def is_effort_variant(self) -> bool:105 return bool(self.effort)106107108def _to_count(num: str, unit: str) -> int:109 mult = {"m": 1e6, "b": 1e9, "t": 1e12}[unit.lower()]110 return int(round(float(num) * mult))111112113def analyze_model_name(raw: str) -> NameAnalysis:114 """Deterministic analysis of a model / repository name."""115 a = NameAnalysis(raw=raw.strip())116 name = a.raw117 if "/" in name and " " not in name.split("/")[0]:118 org, _, rest = name.partition("/")119 a.repo_org, a.repo_name = org.strip(), rest.strip()120 name = rest121 a.from_converter_org = org.strip().lower() in CONVERTER_ORGS122 low = re.sub(r"[\s_]+", "-", name.lower().strip())123 low = re.sub(r"[()\[\]]+", "-", low).strip("-")124 low = re.sub(r"-{2,}", "-", low)125126 # evaluator effort suffixes (up to three, e.g. "-thinking-64k-high-effort"), only when the stem is not itself an official "Thinking" release127 stripped: list[str] = []128 for _ in range(_MAX_EFFORT_SUFFIXES):129 if low in OFFICIAL_THINKING_RELEASES:130 break131 m = _BUDGET_RE.search(low) # "-32k-thinking" before the bare "-thinking"132 if m:133 a.effort = {"reasoning": "on", "thinking_budget": m.group(1).lower(), **a.effort}134 stripped.insert(0, m.group(0)[1:].lower())135 low = low[: m.start()]136 continue137 m = _DEFAULT_THINK_RE.search(low)138 if m:139 a.effort = {"reasoning": "on", **a.effort}140 stripped.insert(0, m.group(1).lower())141 low = low[: m.start()]142 continue143 m = _EFFORT_RE.search(low)144 if m:145 suffix = m.group(1).lower()146 a.effort = {**EFFORT_SUFFIXES[suffix], **a.effort}147 stripped.insert(0, suffix)148 low = low[: m.start()]149 continue150 break151 if stripped:152 a.effort_suffix = "-".join(stripped)153154 # sizes155 am = _ACTIVE_RE.search(low)156 if am:157 a.parameter_count = _to_count(am.group(1), low[am.start(1) + len(am.group(1)):].strip()[0])158 a.active_parameter_count = _to_count(am.group(2), am.group(3))159 else:160 sizes = _SIZE_RE.findall(low)161 if sizes:162 counts = [_to_count(n, u) for n, u in sizes]163 a.parameter_count = max(counts)164165 # dates embedded in the name166 dm = _DATE_RE.search(low)167 if dm:168 a.snapshot_date = f"{dm.group(1)}-{dm.group(2)}-{dm.group(3)}"169 else:170 ym = _YYMM_RE.search(low)171 if ym and not _SIZE_RE.search(ym.group(0)):172 a.snapshot_date = f"20{ym.group(1)}-{ym.group(2)}"173174 # quantisation / precision tokens175 tokens = [t for t in re.split(r"[-_\s./()\[\]]+", low) if t]176 quant: list[str] = []177 precision = None178 kept: list[str] = []179 for t in tokens:180 if t in QUANT_FORMATS:181 quant.append(QUANT_FORMATS[t])182 continue183 if t in PRECISION_FORMATS:184 precision = t185 continue186 if re.fullmatch(r"(w\d+a\d+|q\d(_[a-z0-9]+)*|iq\d(_[a-z0-9]+)*|\d-?bit|int\d|fp\d|nvfp\d|mxfp\d)", t):187 quant.append(QUANT_FORMATS.get(t, "quantized"))188 continue189 kept.append(t)190 # "GGUF" / "MLX" packaging counts as artifact even without a bit-width; "MLX-8bit" is quantised191 a.quant_formats = sorted(set(quant))192 a.precision = precision193 a.is_quantized = any(q not in ("onnx", "coreml", "openvino", "tensorrt", "mlx", "gguf") for q in a.quant_formats) or "gguf" in a.quant_formats194 a.is_conversion = (not a.is_quantized) and (bool(a.quant_formats) or precision is not None or (a.from_converter_org and bool(a.repo_org)))195 if "mlx" in a.quant_formats and any(q.startswith("int") for q in a.quant_formats):196 a.is_quantized = True197198 a.base_key = "-".join(kept).strip("-")199 a.family_hint = family_hint(name)200 return a201202203# ---------------------------------------------------------------------------------------------- family inference204_FAMILY_PATTERNS: list[tuple[re.Pattern[str], str]] = [205 (re.compile(r"\bclaude\b", re.I), "Claude"),206 (re.compile(r"\bgpt[- ]?(oss)\b", re.I), "gpt-oss"),207 (re.compile(r"\b(chat)?gpt[- ]?\d", re.I), "GPT"),208 (re.compile(r"\bo[1-9](-| |$|mini|pro)", re.I), "OpenAI o-series"),209 (re.compile(r"\bgemini\b", re.I), "Gemini"),210 (re.compile(r"\bgemma(?=\d|\b)", re.I), "Gemma"),211 (re.compile(r"\bpalm\b", re.I), "PaLM"),212 (re.compile(r"\bllama(?=\d|\b)", re.I), "Llama"),213 (re.compile(r"\bmistral\b", re.I), "Mistral"),214 (re.compile(r"\bmixtral\b", re.I), "Mixtral"),215 (re.compile(r"\bministral\b", re.I), "Ministral"),216 (re.compile(r"\b(codestral|devstral|magistral|pixtral|voxtral)\b", re.I), None), # own families, name = family217 (re.compile(r"\bqwen(?=\d|\b)|\bqwq\b|\bqvq\b", re.I), "Qwen"),218 (re.compile(r"\bdeepseek\b", re.I), "DeepSeek"),219 (re.compile(r"\bkimi\b", re.I), "Kimi"),220 (re.compile(r"\bglm(?=\d|\b)|\bchatglm\b", re.I), "GLM"),221 (re.compile(r"\bgrok\b", re.I), "Grok"),222 (re.compile(r"\bcommand\b", re.I), "Command"),223 (re.compile(r"\baya\b", re.I), "Aya"),224 (re.compile(r"\bphi(?=\d|\b)", re.I), "Phi"),225 (re.compile(r"\bnemotron\b", re.I), "Nemotron"),226 (re.compile(r"\bgranite\b", re.I), "Granite"),227 (re.compile(r"\bolmo(?=\d|\b)", re.I), "OLMo"),228 (re.compile(r"\bmolmo\b", re.I), "Molmo"),229 (re.compile(r"\bfalcon\b", re.I), "Falcon"),230 (re.compile(r"\byi\b", re.I), "Yi"),231 (re.compile(r"\bminimax\b", re.I), "MiniMax"),232 (re.compile(r"\bhunyuan\b", re.I), "Hunyuan"),233 (re.compile(r"\bernie\b", re.I), "ERNIE"),234 (re.compile(r"\bseed\b", re.I), "Seed"),235 (re.compile(r"\bdoubao\b", re.I), "Doubao"),236 (re.compile(r"\bstep\b", re.I), "Step"),237 (re.compile(r"\binternlm(?=\d|\b)|\binternvl(?=\d|\b)", re.I), "InternLM"),238 (re.compile(r"\bjamba\b", re.I), "Jamba"),239 (re.compile(r"\blfm(?=\d|\b)", re.I), "LFM"),240 (re.compile(r"\bexaone(?=\d|\b)", re.I), "EXAONE"),241 (re.compile(r"\bsolar\b", re.I), "Solar"),242 (re.compile(r"\bnova\b", re.I), "Nova"),243 (re.compile(r"\btitan\b", re.I), "Titan"),244 (re.compile(r"\bsonar\b", re.I), "Sonar"),245 (re.compile(r"\bstable[- ]?diffusion\b|\bsdxl\b|\bsd3\b", re.I), "Stable Diffusion"),246 (re.compile(r"\bflux\b", re.I), "FLUX"),247 (re.compile(r"\bwhisper\b", re.I), "Whisper"),248 (re.compile(r"\bdall[- ]?e\b", re.I), "DALL·E"),249 (re.compile(r"\bsora\b", re.I), "Sora"),250 (re.compile(r"\bveo\b", re.I), "Veo"),251 (re.compile(r"\bimagen\b", re.I), "Imagen"),252 (re.compile(r"\bcogito\b", re.I), "Cogito"),253 (re.compile(r"\bhermes\b", re.I), "Hermes"),254 (re.compile(r"\bsmol(lm|vlm)\b", re.I), "SmolLM"),255 (re.compile(r"\bbert\b", re.I), "BERT"),256 (re.compile(r"\bt5\b", re.I), "T5"),257 (re.compile(r"\bclip\b", re.I), "CLIP"),258 (re.compile(r"\bembed(ding)?\b", re.I), None),259 (re.compile(r"\brerank\b", re.I), None),260]261_FAMILY_VERSION_RE = re.compile(r"^(?P<fam>[A-Za-z][A-Za-z·\-]*?)[\s\-]?(?P<ver>\d+(?:\.\d+)?)", re.I)262263264def family_hint(name: str) -> str | None:265 """Family label without a version ("Qwen", "Llama", "Claude"). Versioned families ("Llama 3.1") are `family_release_hint`."""266 n = name.split("/")[-1]267 for pat, fam in _FAMILY_PATTERNS:268 if pat.search(n):269 if fam is None:270 m = pat.search(n)271 return m.group(0).title() if m else None272 return fam273 return None274275276def family_release_hint(name: str) -> str | None:277 """Versioned family ("Llama 3.1", "Qwen3.6", "Gemini 2.5", "Claude 4") when the name carries a version right after the family word."""278 fam = family_hint(name)279 if not fam:280 return None281 n = name.split("/")[-1]282 # version = digits right after the family word, optionally ".minor" or "-minor" (AA/OpenRouter slugs write 5.4 as 5-4);283 # a number followed by a size unit (38B, 235B, 1.5T) is a parameter count, never a version.284 m = re.search(re.escape(fam.split(" ")[0]) + r"[\s\-]?(\d+)(?![0-9]*\.?\d*[bmt](?![a-z]))(?:[.\-](\d+)(?![0-9]*[bmt](?![a-z])))?(?![0-9])", n, re.I)285 if m:286 version = m.group(1) + (f".{m.group(2)}" if m.group(2) else "")287 joined = fam in ("Qwen", "GLM", "Phi", "Yi", "Step") or re.search(re.escape(fam) + r"\d", n, re.I)288 return f"{fam}{version}" if joined else f"{fam} {version}"289 return fam290291292def variant_key(name: str) -> str:293 """Grouping key for near-duplicates: analysed base key + parameter count, ignoring org prefixes, quant/precision/effort tokens,294 separators and trailing 'instruct/chat/it' words. `Qwen3.6-35B-A3B`, `unsloth/Qwen3.6-35B-A3B-GGUF`, `Qwen3.6 35B A3B FP8` → same key."""295 a = analyze_model_name(name)296 toks = [t for t in a.base_key.split("-") if t and t not in _TRAILING_VARIANT_WORDS]297 return "-".join(toks)298299300# ---------------------------------------------------------------------------------------------- official organisations per family301# family root (lowercase `family_hint`) → organisation slugs / hub org names (lowercase) that publish the *official* checkpoints.302# A repository under one of these orgs is the model (or its official checkpoint), never a third-party artifact.303FAMILY_ORGS: dict[str, tuple[str, ...]] = {304 "llama": ("meta-llama", "meta", "meta-ai", "facebook"), "qwen": ("qwen", "alibaba", "alibaba-cloud", "alibaba-qwen"), "gemma": ("google", "google-deepmind"),305 "gemini": ("google", "google-deepmind"), "palm": ("google",), "claude": ("anthropic",), "gpt": ("openai",), "gpt-oss": ("openai",), "openai o-series": ("openai",),306 "whisper": ("openai",), "dall·e": ("openai",), "sora": ("openai",), "deepseek": ("deepseek-ai", "deepseek"), "kimi": ("moonshotai", "moonshot-ai", "moonshot"),307 "glm": ("zai-org", "thudm", "zhipu-ai", "z-ai", "zhipu"), "mistral": ("mistralai", "mistral-ai", "mistral"), "mixtral": ("mistralai", "mistral-ai", "mistral"),308 "ministral": ("mistralai", "mistral-ai", "mistral"), "codestral": ("mistralai", "mistral-ai", "mistral"), "devstral": ("mistralai", "mistral-ai", "mistral"),309 "magistral": ("mistralai", "mistral-ai", "mistral"), "pixtral": ("mistralai", "mistral-ai", "mistral"), "voxtral": ("mistralai", "mistral-ai", "mistral"),310 "phi": ("microsoft",), "grok": ("xai", "x-ai", "xai-org"), "command": ("cohereforai", "cohere", "coherelabs", "cohere-labs"), "aya": ("cohereforai", "cohere", "coherelabs"),311 "nemotron": ("nvidia",), "granite": ("ibm-granite", "ibm"), "olmo": ("allenai", "ai2"), "molmo": ("allenai", "ai2"), "falcon": ("tiiuae", "tii"), "yi": ("01-ai",),312 "minimax": ("minimaxai", "minimax"), "hunyuan": ("tencent", "tencent-hunyuan"), "ernie": ("baidu",), "seed": ("bytedance-seed", "bytedance"), "doubao": ("bytedance",),313 "step": ("stepfun-ai", "stepfun"), "internlm": ("internlm", "shanghai-ai-laboratory", "opengvlab"), "jamba": ("ai21labs", "ai21"), "lfm": ("liquidai", "liquid-ai"),314 "exaone": ("lgai-exaone", "lg-ai-research"), "solar": ("upstage",), "nova": ("amazon", "aws"), "titan": ("amazon", "aws"), "sonar": ("perplexity-ai", "perplexity"),315 "stable diffusion": ("stabilityai", "stability-ai"), "flux": ("black-forest-labs",), "veo": ("google",), "imagen": ("google",), "cogito": ("deepcogito",),316 "hermes": ("nousresearch",), "smollm": ("huggingfacetb", "hugging-face"), "bert": ("google", "google-bert"), "t5": ("google", "google-t5"), "clip": ("openai",),317}318319320def official_orgs(name: str) -> tuple[str, ...]:321 """Official organisation slugs for the family the name belongs to (empty when unknown)."""322 fam = family_hint(name)323 return FAMILY_ORGS.get(fam.lower(), ()) if fam else ()324325326def is_official_org(repo_org: str | None, name: str) -> bool:327 return bool(repo_org) and repo_org.lower() in official_orgs(name)328329330def effort_config(name: str, config: dict | None) -> dict:331 """Result configuration for an evaluation-effort variant: the effort dict (`reasoning_effort`, `reasoning`, `thinking_budget`) is merged332 into the config and the evaluator's variant slug is kept as `aa_variant_slug` so folded results stay distinguishable. Names that are333 not effort variants return the config unchanged."""334 cfg = dict(config or {})335 a = analyze_model_name(name)336 if not a.is_effort_variant:337 return cfg338 for k, v in a.effort.items():339 cfg.setdefault(k, v)340 slug = cfg.get("aa_slug") or a.raw.split("/")[-1].strip().lower().replace(" ", "-")341 cfg.setdefault("aa_variant_slug", slug)342 return cfg343344345def base_name(name: str) -> str:346 """The name with the evaluator effort suffix removed ("gpt-5-4-mini-medium" → "gpt-5-4-mini", "Claude Sonnet 4 (no thinking)" →347 "Claude Sonnet 4"); unchanged when not a variant."""348 a = analyze_model_name(name)349 if not a.is_effort_variant or not a.effort_suffix:350 return name.strip()351 n = name.strip()352 # the suffix was detected on a normalised form (spaces/underscores/parentheses → "-"): strip it from the raw name with the same tolerance353 tokens = [re.escape(t) for t in a.effort_suffix.split("-") if t]354 pattern = r"[\s\-_(]+" + r"[\s\-_]*".join(tokens) + r"[)\s]*$"355 out = re.sub(pattern, "", n, flags=re.I)356 return out.strip().rstrip("-_ (").strip() or n357358359__all__ = ["ARTIFACT_PACKAGING", "CONVERTER_ORGS", "EFFORT_SUFFIXES", "FAMILY_ORGS", "NameAnalysis", "PRECISION_FORMATS", "QUANT_FORMATS", "analyze_model_name",360 "base_name", "effort_config", "family_hint", "family_release_hint", "is_official_org", "official_orgs", "variant_key"]361