modeldna / scan.py
trohrbaugh's picture
KNOWN_BASES: add Cactus Needle
e1cade2 verified
Raw
History Blame Contribute Delete
50 kB
#!/usr/bin/env python3
"""
modeldna Stage 1 HF Scanner β€” core logic.
Given a HuggingFace model_id, validates architectural claims against the
ModelAtlas reference database. No weight download needed β€” uses config.json only.
This is the heart of the modeldna 'test before you download' feature.
"""
from __future__ import annotations
import json, hashlib, os, re, time
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
import requests
HF_API = "https://huggingface.co"
HF_DATASET = "RadicalNotionAI/modelatlas-reference"
DB = "postgresql:///modelatlas?host=/var/run/postgresql&port=5433&user=tim"
# In-process cache β€” loaded once per worker, refreshes when the file changes
_REF_DF = None
_REF_LOADED_AT: float = 0.0
_REF_TTL = 3600 # reload at most once per hour
def _load_reference_df():
"""Load ModelAtlas reference parquet. Tries local snapshot first, then HF dataset."""
global _REF_DF, _REF_LOADED_AT
now = time.time()
if _REF_DF is not None and (now - _REF_LOADED_AT) < _REF_TTL:
return _REF_DF
import pandas as pd
# 1. Local snapshot (fast, used in dev / on local server)
local_path = Path(__file__).parent.parent / "snapshots" / "modeldna_reference.parquet"
if local_path.exists():
try:
_REF_DF = pd.read_parquet(local_path)
_REF_LOADED_AT = now
return _REF_DF
except Exception:
pass
# 2. HF dataset (used on HF Space β€” downloaded and cached by huggingface_hub)
try:
from huggingface_hub import hf_hub_download
path = hf_hub_download(
repo_id=HF_DATASET,
filename="modeldna_reference.parquet",
repo_type="dataset",
)
_REF_DF = pd.read_parquet(path)
_REF_LOADED_AT = now
return _REF_DF
except Exception:
pass
return None
def reference_count() -> int:
"""Number of models in the ModelAtlas fingerprint reference (for the 'how we know' moat line)."""
try:
df = _load_reference_df()
return int(len(df)) if df is not None else 0
except Exception:
return 0
# Known base model reference configs (canonical identifiers)
KNOWN_BASES = {
"qwen3_5_text": {
"name": "Qwen3.5 (dense)",
"vocab_size": 248320,
"model_type_patterns": ["qwen3_5_text", "qwen3_5"],
},
"qwen3_5_moe_text": {
"name": "Qwen3.5 MoE",
"vocab_size": 248320,
"model_type_patterns": ["qwen3_5_moe_text", "qwen3_5_moe"],
},
"qwen3": {
"name": "Qwen3",
"vocab_size": [151936, 152064, 151851, 151670],
"model_type_patterns": ["qwen3"],
# 151936/152064 = standard Qwen3; 151851 = BAAI OpenSeek (domain token swap);
# 151670 = OpenBMB SciCore-Mol (chemistry tokenizer variant)
},
"qwen2": {
"name": "Qwen2.5 (incl. VL)",
"vocab_size": [151936, 152064, 151680],
"model_type_patterns": ["qwen2"],
# 151680 = MiMo-Embodied-7B uses Qwen2.5-VL backbone with this vocab
},
"llama3": {
"name": "Llama 3.x",
"vocab_size": 128256,
"model_type_patterns": ["llama"],
"num_key_value_heads_hint": [8, 32],
},
"llama2": {
"name": "Llama 2",
"vocab_size": 32000,
"model_type_patterns": ["llama"],
},
"mistral": {
"name": "Mistral 7B family",
"vocab_size": 32000,
"model_type_patterns": ["mistral", "mixtral"],
},
"deepseek_v3": {
"name": "DeepSeek V3/R1",
"vocab_size": 129280,
"model_type_patterns": ["deepseek_v3", "deepseek_v2"],
"kv_lora_rank": 512,
},
"gemma": {
"name": "Gemma family",
"vocab_size": [256000, 262144],
"model_type_patterns": ["gemma"],
},
"nemotron_h": {
"name": "NemotronH (NVIDIA Mamba+MoE hybrid)",
"vocab_size": 131072,
"model_type_patterns": ["nemotron_h", "nemotronh"],
},
"ministral3": {
"name": "Mistral 3.x (medium/large dense)",
"vocab_size": 131072,
"model_type_patterns": ["ministral3", "mistral3"],
# Mistral Medium 3.5, hidden=12288, 88 layers β€” dense ~128B
# Multimodal wrapper uses model_type=mistral3; LLM backbone is ministral3
# vocab 131072 overlaps with NemotronH β€” exact model_type match scores higher
},
"glm4": {
"name": "ZhipuAI GLM-4.x (4.5 / 4.6 / 4.7 / 4.6V text backbone)",
"vocab_size": [151552, 151936, 154880],
"model_type_patterns": ["glm4v_moe_text", "glm4v_moe", "glm4_moe_lite", "glm4_moe", "glm4", "chatglm"],
# 151552 = GLM-4.5/4.6 dense+MoE and 4.6V multimodal text backbone
# 154880 = GLM-4.7 series (including 4.7-Flash, glm4_moe_lite)
},
"seed_oss": {
"name": "ByteDance Seed-OSS (dense)",
"vocab_size": 155136,
"model_type_patterns": ["seed_oss"],
# Dense GQA, RoPE ΞΈ=1e7, 512K context, 80β†’8 KV heads
},
"bailing_v2": {
"name": "AntGroup Bailing-V2 / V2.5 (inclusionAI Ling)",
"vocab_size": 157184,
"model_type_patterns": ["bailing_hybrid", "bailing_moe", "bailingmm_moe_v2_lite"],
# V2 = bailing_moe; V2.5 = bailing_hybrid (MLA + linear-attn + MTP)
# bailingmm_moe_v2_lite = Ming-flash-omni multimodal lite variant
},
"llada2": {
"name": "inclusionAI LLaDA2 (discrete-diffusion MoE)",
"vocab_size": [157184, 173568],
"model_type_patterns": ["llada2_moe", "llada2"],
# 157184 = text-only discrete diffusion (flash, base)
# 173568 = Uni any-to-any variant β€” adds ~16K image codebook tokens to vocab
# Non-autoregressive masked LM; separate family from Bailing-V2 by training paradigm
},
"kimi": {
"name": "Moonshot Kimi (K2, Kimi-Linear)",
"vocab_size": 163840,
"model_type_patterns": ["kimi_linear", "kimi"],
# Kimi-Linear adds linear_attn_config + MLA + MTP on Kimi MoE backbone
},
"ernie4_5_moe": {
"name": "Baidu ERNIE 4.5 (text MoE)",
"vocab_size": 103424,
"model_type_patterns": ["ernie4_5_moe"],
# Text MoE line: 21B-A3B (28L, hidden 2560), 300B-A47B. Shares the 103424 ERNIE
# tokenizer with the VL line. exact `ernie4_5_moe` scores above the VL base's
# startswith on `ernie4_5_moe_vl`, so VL models still resolve to ernie4_5_vl.
},
"ernie4_5_vl": {
"name": "Baidu ERNIE 4.5 VL (MoE multimodal)",
"vocab_size": 103424,
"model_type_patterns": ["ernie4_5_moe_vl", "ernie4_5_vl"],
},
"qianfan_vl": {
"name": "Baidu Qianfan-VL (dense multimodal)",
"vocab_size": 182025,
"model_type_patterns": ["qianfanvl_chat", "qianfan"],
# Distinct Baidu tokenizer from ERNIE β€” two separate VLM lineages
# model_type is qianfanvl_chat; qianfan prefix catches future variants
},
"interns1": {
"name": "InternLM S1 (dense, long-chain reasoning)",
"vocab_size": 153216,
"model_type_patterns": ["interns1"],
},
"pangu_pro_moe": {
"name": "FreedomIntelligence Pangu-R (Huawei Pangu-Pro-MoE)",
"vocab_size": 153600,
"model_type_patterns": ["pangupromoe"],
# model_type in config is "PanguProMoE" β€” lowercased to pangupromoe for matching
# MoE 80/8, first_k_dense_replace=4, hidden=4608, layers=50
},
"iquest_coder": {
"name": "IQuest-Coder",
"vocab_size": 76800,
"model_type_patterns": ["iquestcoder"],
# Code-specialized tokenizer (76800 = code-token-dense). Dense GQA 32β†’2.
# Same family across 7B (14 layers) and 40B (80 layers).
},
"minicpm": {
"name": "OpenBMB MiniCPM",
"vocab_size": 73448,
"model_type_patterns": ["minicpm"],
# MiniCPM family (AgentCPM-Report etc.). Heavy GQA 32β†’2.
},
"step3_5": {
"name": "StepFun Step-3.5 Flash",
"vocab_size": [128815, 128896],
"model_type_patterns": ["step3p5"],
# Per-layer RoPE schedule: every 4th layer gets long-context theta (1e6/5e6),
# others get 1e4. Sliding-window=512. First StepFun entry with multi-freq RoPE.
},
"mimo_v2": {
"name": "Xiaomi MiMo V2.x",
"vocab_size": 152576,
"model_type_patterns": ["mimo_v2"],
# V2.5: hidden=4096, 48 layers; V2.5-Pro: hidden=6144, 70 layers
},
"emu3": {
"name": "BAAI Emu3 family (unified vision+text)",
"vocab_size": [184622, 282926],
"model_type_patterns": ["emu3"],
# Emu3-Stage1 vocab=184622; Emu3.5 vocab=282926 (expanded vision codebook)
# Emu3.5 also adds hidden 4096β†’5120, layers 32β†’64, sliding_window=4096
},
"hunyuan_v1": {
"name": "Tencent Hunyuan V1 (dense + MoT multimodal)",
"vocab_size": 120818,
"model_type_patterns": ["hunyuan_v1_dense", "hunyuan_vl_mot", "hunyuan"],
# Catches HY-Embodied-0.5 and HY-1.8B variants; MoT = Mixture of Tokens
},
"gpt_oss": {
"name": "OpenAI gpt-oss (via InternVL3.5 wrapper)",
"vocab_size": 200028,
"model_type_patterns": ["gpt_oss"],
# Caught via lifted text_config; InternVL3.5-GPT-OSS-20B uses this backbone
},
"valley": {
"name": "ByteDance Valley (video-language)",
"vocab_size": [151675, 151679],
"model_type_patterns": ["valley"],
# Valley-Eagle-7B (151675) and Valley2.5 (151679) β€” close but distinct vocabs
},
"starcoder2": {
"name": "BigCode StarCoder2",
"vocab_size": 49152,
"model_type_patterns": ["starcoder2", "gpt_bigcode"],
# 3B: hidden=3072/30L (97K dl), 7B: hidden=4608/32L, 15B: hidden=6144/40L
# gpt_bigcode = tiny_starcoder_py and early StarCoder variants (234K dl)
# Code-specialized tokenizer (49152 tokens)
},
"zaya": {
"name": "Zyphra ZAYA1 (Global Attn + SWA + MoE)",
"vocab_size": 262272,
"model_type_patterns": ["zaya", "zaya1_vl"],
# Global attention alternating with Sliding Window Attention (4K window) + MoE
# CCA (Cached Context Attention) is Zyphra's proprietary KV-efficient attn variant
# 8B: hidden=2048, 80 layers, 16 experts, 4B active
# 74B: hidden=4096, 120 layers, 24 experts, 4B active, GQA 16β†’2
# Depth (120L at 74B) enabled by SWA β€” halves KV cache vs all-global attention
# vocab β‰ˆ Gemma tokenizer + 128 extra tokens; trained on AMD MI300x
# 74B-Preview is pre-RL reasoning base (no RLHF/instruct tuning)
},
"zamba2": {
"name": "Zyphra Zamba2 (Mamba2 + shared-attention hybrid)",
"vocab_size": [32000, 32064],
"model_type_patterns": ["zamba2"],
# SSM hybrid: majority Mamba2 blocks with a single shared full-attention block
# applied periodically via skip connections. 1.2B (38L, hidden 2048),
# 7B (81L, hidden 3584). vocab 32000 (Llama-2 tokenizer); VL variant 32064.
},
"hunyuan_v3": {
"name": "Tencent Hunyuan V3 / Hy-MT2 (MoE)",
"vocab_size": 120832,
"model_type_patterns": ["hy_v3"],
# Hy-MT2 series: 1.8B dense, 7B dense, 30B-A3B MoE (128E/8A).
# 48 layers, hidden=2048 for MoE tier. QK norm. HYV3ForCausalLM.
},
"mellum": {
"name": "JetBrains Mellum (code-specialized MoE)",
"vocab_size": 98304,
"model_type_patterns": ["mellum"],
# Mellum2-12B-A2.5B: 28 layers all-MoE from layer 0, 64E/8A, GQA 32q/4kv (8:1),
# QK norm, custom MellumTopKRouter, 128K context, hidden=2304.
# vocab 98304 = 384Γ—256 β€” code-optimized tokenizer (JetBrains IDE corpora).
# Mellum-4B (prior gen): 3.4B dense, 30 layers, hidden=3072, no MoE.
},
"plamo": {
"name": "Preferred Networks PLaMo (plamo / plamo2 / plamo3, JP)",
"vocab_size": [50112, 100000, 107520],
"model_type_patterns": ["plamo3", "plamo2", "plamo"],
# PlamoForCausalLM (100B, vocab 50112) / Plamo2ForCausalLM (plamo-2-8b, vocab 100000,
# Samba-style Mamba+attention hybrid) / Plamo3ForCausalLM (plamo-3-nict-2b/8b, vocab 107520).
# Gated repos; config-only scan needs auth (public Space returns None for gated).
},
"inkling": {
"name": "Thinking Machines Inkling (omni-modal MoE)",
"vocab_size": 201024,
"model_type_patterns": ["inkling_mm_model", "inkling"],
# Thinking Machines Lab debut (ex-OpenAI team), 2026-07. ~974B total / ~40B active sparse MoE:
# 256 routed experts (6 active + 2 shared), 8 MTP layers, GQA (NOT MLA), hidden 6144, 66L.
# Omni-modal (text+vision+audio), InklingForConditionalGeneration.
# vocab 201024 + eos 200006 = OpenAI o200k/harmony tokenizer heritage β€” the ex-OpenAI tell.
},
"needle": {
"name": "Cactus Needle (26M pure-attention encoder-decoder, edge tool-calling)",
"vocab_size": 8192,
"model_type_patterns": ["needle"],
# Cactus Compute Needle, NeedleForCausalLM (actually encoder-decoder): 12 enc + 8 dec layers,
# PURE ATTENTION (no FFN), GQA 8H/4KV, RoPE, ZCRMSNorm (zero-centered), gated residuals,
# d_model 512, vocab 8192 SentencePiece, ~26M params, INT4 QAT. Distilled from Gemini 3.1
# (honestly disclosed β€” distillation leaves no weight-level trace). On-device function-calling.
},
}
def fetch_config(model_id: str) -> Optional[dict]:
"""Fetch config.json from HuggingFace. Returns None on failure."""
url = f"{HF_API}/{model_id}/resolve/main/config.json"
try:
headers = {}
token = os.environ.get("HF_TOKEN", "")
if token:
headers["Authorization"] = f"Bearer {token}"
r = requests.get(url, headers=headers, timeout=20)
r.raise_for_status()
return r.json()
except Exception as e:
return None
def fetch_model_metadata(model_id: str) -> dict:
"""Fetch HF model metadata (downloads, likes, author, tags, file list)."""
try:
headers = {}
token = os.environ.get("HF_TOKEN", "")
if token:
headers["Authorization"] = f"Bearer {token}"
r = requests.get(f"{HF_API}/api/models/{model_id}", headers=headers, timeout=10)
r.raise_for_status()
d = r.json()
return {
"downloads": d.get("downloads", 0),
"likes": d.get("likes", 0),
"author": d.get("author", ""),
"tags": d.get("tags", []),
"pipeline_tag": d.get("pipeline_tag", ""),
"base_model": d.get("cardData", {}).get("base_model", ""),
"license": d.get("cardData", {}).get("license", ""),
"created_at": d.get("createdAt", ""),
"last_modified": d.get("lastModified", ""),
"files": [s.get("rfilename", "") for s in d.get("siblings", [])],
}
except Exception:
return {}
# ── GGUF support (header-only; no weight download / dequantization) ───────────
import struct
_GGUF_MAGIC = b"GGUF"
# value type -> fixed byte size (None = variable: STRING/ARRAY handled specially)
_GGUF_SCALAR = {0: 1, 1: 1, 2: 2, 3: 2, 4: 4, 5: 4, 6: 4, 7: 1, 10: 8, 11: 8, 12: 8}
_GGUF_STRING, _GGUF_ARRAY = 8, 9
def _gguf_read_str(buf, off):
(ln,) = struct.unpack_from("<Q", buf, off); off += 8
s = buf[off:off + ln].decode("utf-8", "replace"); off += ln
return s, off
def _gguf_skip_value(buf, off, vtype):
"""Advance past a value of the given type; return (extracted_string_or_None, new_off)."""
if vtype in _GGUF_SCALAR:
return None, off + _GGUF_SCALAR[vtype]
if vtype == _GGUF_STRING:
s, off = _gguf_read_str(buf, off)
return s, off
if vtype == _GGUF_ARRAY:
(etype,) = struct.unpack_from("<I", buf, off); off += 4
(cnt,) = struct.unpack_from("<Q", buf, off); off += 8
for _ in range(cnt):
if etype in _GGUF_SCALAR:
off += _GGUF_SCALAR[etype]
elif etype == _GGUF_STRING:
_, off = _gguf_read_str(buf, off)
else:
raise ValueError(f"nested array type {etype}")
return None, off
raise ValueError(f"unknown gguf value type {vtype}")
def parse_gguf_metadata(buf: bytes) -> dict:
"""Parse GGUF metadata KV pairs from the file header bytes. Returns {key: str_value}
for string-valued keys (architecture, name, chat_template, etc.). Tolerates truncation."""
if buf[:4] != _GGUF_MAGIC:
return {}
off = 4
(_ver,) = struct.unpack_from("<I", buf, off); off += 4
off += 8 # tensor_count (uint64) β€” skip
(kv_count,) = struct.unpack_from("<Q", buf, off); off += 8
out = {}
for _ in range(kv_count):
try:
key, off = _gguf_read_str(buf, off)
(vtype,) = struct.unpack_from("<I", buf, off); off += 4
val, off = _gguf_skip_value(buf, off, vtype)
if val is not None:
out[key] = val
except (struct.error, ValueError, IndexError):
break # ran past the fetched header window β€” return what we have
return out
def fetch_gguf_metadata(model_id: str, max_bytes: int = 12_000_000) -> dict:
"""Find a .gguf file in the repo and range-fetch its header to parse metadata.
No full download. Returns {} on failure."""
headers = {}
token = os.environ.get("HF_TOKEN", "")
if token:
headers["Authorization"] = f"Bearer {token}"
try:
r = requests.get(f"{HF_API}/api/models/{model_id}", headers=headers, timeout=10)
files = [s["rfilename"] for s in r.json().get("siblings", [])]
except Exception:
return {}
ggufs = [f for f in files if f.lower().endswith(".gguf")]
# prefer a single-file / first shard
ggufs.sort(key=lambda f: ("00001" not in f, f))
for f in ggufs[:1]:
try:
h = dict(headers); h["Range"] = f"bytes=0-{max_bytes}"
rr = requests.get(f"{HF_API}/{model_id}/resolve/main/{f}", headers=h, timeout=30)
if rr.status_code in (200, 206):
md = parse_gguf_metadata(rr.content)
if md:
md["_gguf_file"] = f
return md
except Exception:
continue
return {}
def resolve_gguf_source(model_id: str, metadata: dict, gguf_md: dict) -> Optional[str]:
"""Best-effort upstream full-weights repo for a GGUF quant."""
# 1) explicit base_model tag on the GGUF repo
bm = metadata.get("base_model")
if bm:
return bm if isinstance(bm, str) else (bm[0] if bm else None)
# 2) GGUF metadata general.base_model
for k in ("general.base_model", "general.base_model.0.repo_url", "general.source.repo"):
if gguf_md.get(k):
v = gguf_md[k]
return v.split("huggingface.co/")[-1].strip("/") if "huggingface.co" in v else v
# 3) name heuristic: strip common quant/format suffixes
name = model_id.split("/")[-1]
stripped = re.sub(r"[-_.]?(GGUF|gguf|Q\d[_A-Za-z0-9]*|i1|IQ\d[_A-Za-z0-9]*|imatrix|"
r"fp16|bf16|f16|MLX|mlx|AWQ|GPTQ|NVFP4|mxfp\d+|HyperQuant|SHQ8|ROCmFP4|COHERENT)$",
"", name)
while stripped != name: # strip repeated suffixes
name, stripped = stripped, re.sub(
r"[-_.]?(GGUF|gguf|Q\d[_A-Za-z0-9]*|i1|IQ\d[_A-Za-z0-9]*|imatrix|fp16|bf16|f16|"
r"MLX|mlx|AWQ|GPTQ|NVFP4|mxfp\d+|HyperQuant|SHQ8|ROCmFP4|COHERENT)$", "", stripped)
org = model_id.split("/")[0]
return f"{org}/{stripped}" if stripped and stripped != model_id.split("/")[-1] else None
def fetch_chat_template(model_id: str) -> str:
"""Fetch the chat template (config-only, no weights). Checks tokenizer_config.json's
chat_template field and the standalone chat_template.jinja. Returns "" on failure."""
headers = {}
token = os.environ.get("HF_TOKEN", "")
if token:
headers["Authorization"] = f"Bearer {token}"
parts = []
for fname in ("tokenizer_config.json", "chat_template.jinja"):
try:
r = requests.get(f"{HF_API}/{model_id}/resolve/main/{fname}", headers=headers, timeout=15)
if r.status_code != 200:
continue
if fname.endswith(".json"):
ct = r.json().get("chat_template", "")
if isinstance(ct, list): # some templates are lists of {name, template}
ct = " ".join(str(x.get("template", "")) for x in ct)
parts.append(ct or "")
else:
parts.append(r.text)
except Exception:
continue
return "\n".join(p for p in parts if p)
# Base-org / lab names whose denial in a system prompt indicates provenance concealment
_BASE_ORG_TERMS = [
"qwen", "alibaba", "llama", "meta", "mistral", "mixtral", "deepseek",
"gemma", "google", "gpt", "openai", "anthropic", "claude", "yi", "01-ai",
"phi", "microsoft", "falcon", "glm", "zhipu", "baichuan", "internlm",
]
# Instruction verbs that, combined with a base-org name, indicate an identity scrub
_SCRUB_DENIAL = re.compile(
r"(never|do not|don'?t|must not|should not|refuse to)\s+(claim|say|state|admit|identify|mention|reveal|acknowledge)",
re.IGNORECASE,
)
_SCRUB_ASSERT = re.compile(
r"(always identify (your ?self|as)|your (name|identity) is|you (are|were) (created|developed|made|built|trained) by)",
re.IGNORECASE,
)
_SCRUB_PERMANENCE = re.compile(
r"(permanent|override[s]? (conflicting|any|all)|regardless of|even if (asked|instructed)|cannot be changed)",
re.IGNORECASE,
)
def detect_provenance_scrubbing(chat_template: str) -> Optional[dict]:
"""Stage-1 detector: does the chat template inject a default system prompt that
instructs the model to DENY its true origin (name a base org after a denial verb)?
This is the Qwythos pattern. Returns a flag dict or None."""
if not chat_template:
return None
text = chat_template
has_denial = bool(_SCRUB_DENIAL.search(text))
has_assert = bool(_SCRUB_ASSERT.search(text))
has_perm = bool(_SCRUB_PERMANENCE.search(text))
# which base orgs are named in the template
orgs = sorted({t for t in _BASE_ORG_TERMS if re.search(r"\b" + re.escape(t) + r"\b", text, re.IGNORECASE)})
# Strong signal: a denial verb AND >=2 base-org names named for denial (e.g. "never claim
# to be Qwen, Alibaba, OpenAI, Anthropic") β€” an explicit lineage scrub.
if has_denial and len(orgs) >= 2:
return {
"type": "PROVENANCE_SCRUBBING",
"severity": "HIGH",
"orgs_denied": orgs,
"explanation": (
"The chat template injects a default system prompt that instructs the model to "
f"DENY its origin β€” it names {', '.join(orgs)} in a 'never claim to be…' style "
"instruction" + (", marked permanent/overriding user instructions" if has_perm else "")
+ ". Every user of the standard chat template inherits this. This actively "
"conceals the model's true base lineage at inference time."
),
}
# Weaker signal: hard-coded identity assertion with permanence, no explicit denial
if has_assert and has_perm:
return {
"type": "IDENTITY_OVERRIDE",
"severity": "MEDIUM",
"explanation": (
"The chat template hard-codes a permanent, override-all identity in its default "
"system prompt. Not necessarily deceptive, but it prevents the model from "
"reporting its own provenance and overrides user/system instructions."
),
}
return None
# ── DNA-001: provenance risk scoring ──────────────────────────────────────────
# Points per flag type (heaviest = active deception). Score clamps to 100; 0 flags = 0 = clean.
_FLAG_WEIGHTS = {
"PROVENANCE_SCRUBBING": 45, # actively hides its lineage β€” worst
"IMPOSSIBLE_WEIGHT_PROVENANCE": 30, # claims a closed brand it can't be built on
"NAMING_MISATTRIBUTION": 20, # sells a brand that isn't the base
"IDENTITY_OVERRIDE": 15, # hard-coded identity, not necessarily deceptive
"NAME_MISMATCH": 15,
"UNVERIFIABLE_CLAIM": 10,
}
def _risk_score(flags: list) -> tuple:
"""Aggregate flags into a 0–100 score + band. Higher = more provenance risk."""
score = 0
for f in flags:
w = _FLAG_WEIGHTS.get(f.get("type"), 8)
if f.get("severity") == "MEDIUM":
w = int(w * 0.85)
score += w
score = min(100, score)
band = ("CLEAN" if score == 0 else "LOW" if score <= 25
else "MODERATE" if score <= 55 else "HIGH")
return score, band
def _engine_panel(flags: list, arch_state: str, chat_template_seen: bool) -> list:
"""DNA-003: group detectors into named 'engines' with per-engine verdicts (VirusTotal-style).
arch_state: 'confirmed' | 'original' | 'new' | 'unknown'."""
by_type = {f.get("type") for f in flags}
def verdict(triggered, ran=True):
return "πŸ”΄ flagged" if triggered else ("βœ… pass" if ran else "βž– not run")
arch_row = {
"confirmed": ("βœ… pass", "base architecture identified against ModelAtlas reference"),
"original": ("βœ… pass", "recognized original architecture, catalogued in ModelAtlas"),
"new": ("πŸ†• new", "novel architecture from a known lab; not yet catalogued"),
"unknown": ("❓ unrecognized", "no known base match"),
}.get(arch_state, ("❓ unrecognized", "no known base match"))
return [
{"engine": "Architecture Match", "status": arch_row[0], "detail": arch_row[1]},
{"engine": "Provenance Integrity",
"status": verdict({"PROVENANCE_SCRUBBING", "IDENTITY_OVERRIDE"} & by_type, chat_template_seen),
"detail": "chat-template lineage scrub / identity override"},
{"engine": "Naming & Attribution",
"status": verdict({"NAMING_MISATTRIBUTION", "NAME_MISMATCH", "IMPOSSIBLE_WEIGHT_PROVENANCE",
"UNVERIFIABLE_CLAIM"} & by_type),
"detail": "name vs actual base; closed-brand / impossible claims"},
{"engine": "License Compliance", "status": "βž– not run",
"detail": "base-license vs derivative usage β€” coming soon"},
{"engine": "Safety Profile", "status": "βž– not run",
"detail": "behavioral refusal / safety analysis (Stage 2) β€” coming soon"},
]
# Closed-weight brands: no weights ever released, so weight transfer is IMPOSSIBLE (not merely
# unverifiable). Distillation-via-outputs is the only possible link and leaves no weight trace.
_CLOSED_WEIGHT_BRANDS = {
"claude": "Anthropic Claude", "anthropic": "Anthropic Claude",
"mythos": "Anthropic Mythos (unreleased)", "fable": "Anthropic Fable (released; weights closed)",
"gpt": "OpenAI GPT", "chatgpt": "OpenAI GPT", "openai": "OpenAI GPT",
"gemini": "Google Gemini", "grok": "xAI Grok",
}
def detect_claimed_base(model_id: str, config: dict, metadata: dict) -> dict:
"""Detect what base model a model claims to be derived from."""
claims = {}
name = model_id.split("/")[-1].lower()
# Explicit base_model field
if metadata.get("base_model"):
claims["explicit_base"] = metadata["base_model"]
# Name-based detection
name_signals = []
for term, base_key in [
("qwen3.5", "qwen3_5"), ("qwen3-5", "qwen3_5"), ("qwen35", "qwen3_5"),
("qwen3", "qwen3"), ("qwen2.5", "qwen2"), ("qwen2", "qwen2"),
("llama-3", "llama3"), ("llama3", "llama3"), ("llama-2", "llama2"),
("mistral", "mistral"), ("mixtral", "mistral"),
("deepseek", "deepseek_v3"), ("gemma", "gemma"),
]:
if term in name:
name_signals.append(base_key)
if name_signals:
claims["name_implies"] = name_signals
# Suspicious claims in name
suspicious = []
for term in ["claude", "gpt", "chatgpt", "openai", "gemini", "anthropic", "mythos", "fable"]:
if term in name:
suspicious.append(term)
if suspicious:
claims["suspicious_name_terms"] = suspicious
return claims
def stage1_screen(model_id: str, config: dict) -> dict:
"""
Stage 1: Architecture screening against ModelAtlas reference.
Returns a structured verdict without downloading any weights.
Handles nested text_config (Qwen3.5/3.6, Mistral3, MiMo-V2.5 pattern).
"""
# Lift nested LLM config into top-level when top-level vocab/hidden is absent.
# Recurse up to 2 levels deep to handle models like Logics-MLLM where LLM backbone
# is at thinker_config.text_config (two levels: thinker_config β†’ text_config).
_NESTED_KEYS = ("text_config", "llm_config", "thinker_config", "language_model")
_SKIP_KEYS = ("text_config", "llm_config", "thinker_config", "language_model",
"vision_config", "audio_config", "sound_config")
if not config.get("vocab_size"):
for nested_key in _NESTED_KEYS:
candidate = config.get(nested_key, {})
if candidate:
# One level deep
if candidate.get("vocab_size"):
# Let nested model_type win over top-level wrapper type
outer = {k: v for k, v in config.items()
if k not in _SKIP_KEYS and k != "model_type"}
config = {**outer, **candidate}
break
# Two levels deep (e.g. thinker_config.text_config)
for inner_key in _NESTED_KEYS:
inner = candidate.get(inner_key, {})
if inner and inner.get("vocab_size"):
outer = {k: v for k, v in config.items()
if k not in _SKIP_KEYS and k != "model_type"}
config = {**outer, **inner}
break
else:
continue
break
vocab = config.get("vocab_size")
model_type = (config.get("model_type") or "").lower()
hidden = config.get("hidden_size")
layers = config.get("num_hidden_layers")
kv_lora = config.get("kv_lora_rank") # MLA signal
base_model_field = config.get("base_model") or config.get("_name_or_path", "")
# Compute architecture signature
key_fields = sorted([
f"vocab={vocab}", f"type={model_type}", f"hidden={hidden}",
f"layers={layers}", f"kv_lora={kv_lora}",
])
arch_sig = hashlib.md5("|".join(str(f) for f in key_fields).encode()).hexdigest()[:12]
# Match against known bases
base_matches = []
for base_key, base_info in KNOWN_BASES.items():
score = 0
reasons = []
# Vocab match
expected_vocab = base_info.get("vocab_size")
if isinstance(expected_vocab, list):
if vocab in expected_vocab: score += 3; reasons.append(f"vocab matches ({vocab})")
elif vocab == expected_vocab:
score += 3; reasons.append(f"vocab matches ({vocab})")
# Model type match
for pat in base_info.get("model_type_patterns", []):
if model_type == pat:
score += 3; reasons.append(f"model_type '{model_type}' exact"); break
elif model_type.startswith(pat):
score += 2; reasons.append(f"model_type '{model_type}' matches {pat}"); break
# MLA signal
if base_key == "deepseek_v3" and kv_lora and kv_lora > 0:
score += 2; reasons.append(f"MLA kv_lora_rank={kv_lora}")
if score >= 3:
base_matches.append({
"base": base_key,
"name": base_info["name"],
"confidence": "HIGH" if score >= 5 else "MODERATE",
"score": score,
"evidence": reasons,
})
# Is the scanned model itself catalogued in the ModelAtlas reference? Original /
# first-party architectures (e.g. poolside/Laguna) are NOT derivatives of a known base;
# recognize them as catalogued originals rather than labelling them "unrecognized".
self_catalogued = None
try:
ref = _load_reference_df()
if ref is not None:
row = ref[ref["model_id"].str.lower() == model_id.lower()]
if len(row):
r0 = row.iloc[0]
def _clean(x): # NaN-safe: reference cols can be float('nan')
return x.strip() if isinstance(x, str) and x.strip() else None
self_catalogued = {
"model_id": model_id,
"org": _clean(r0.get("org_display")) or _clean(r0.get("org")),
"model_type": model_type or _clean(r0.get("model_type")),
}
except Exception:
pass
# DNA-016: is the publisher a known lab? (β‰₯3 other models catalogued in the reference)
lab_catalogued = None
try:
if ref is not None and "/" in model_id:
org = model_id.split("/")[0]
n = int(ref["model_id"].str.lower().str.startswith(org.lower() + "/").sum())
if n >= 3:
lab_catalogued = {"org": org, "model_count": n}
except Exception:
pass
# Query ModelAtlas reference parquet for architecturally similar models
db_matches = []
try:
ref = _load_reference_df()
if ref is not None and vocab and hidden:
hit = ref[
(ref["vocab_size"] == vocab) &
(ref["hidden_size"] == hidden) &
(~ref["model_id"].str.contains("tiny", case=False, na=False)) &
(~ref["model_id"].str.startswith("/", na=False)) &
(ref["model_id"].str.lower() != model_id.lower())
].sort_values("hf_downloads", ascending=False).head(5)
db_matches = hit[
["model_id", "org_display", "hf_downloads", "total_params",
"technique_signature", "num_layers", "hidden_size", "vocab_size"]
].rename(columns={"org_display": "lab"}).to_dict("records")
except Exception:
pass
# Also try local DB if available (dev / local server)
if not db_matches:
try:
import psycopg2, psycopg2.extras
conn = psycopg2.connect(DB)
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cur.execute("""
SELECT m.model_id, o.name AS lab, m.hf_downloads, m.release_date,
a.technique_signature, a.total_params, a.num_layers, a.hidden_size, a.vocab_size
FROM analyses a JOIN models m ON m.id=a.model_id
JOIN organizations o ON m.org_id=o.id
WHERE a.is_current=true AND a.vocab_size=%s AND a.hidden_size=%s
AND m.model_id NOT ILIKE '%%tiny%%' AND m.model_id NOT ILIKE '/%%'
ORDER BY m.hf_downloads DESC NULLS LAST
LIMIT 5
""", (vocab, hidden))
db_matches = [dict(r) for r in cur.fetchall()]
cur.close(); conn.close()
except Exception:
pass
return {
"arch_signature": arch_sig,
"config_signals": {
"model_type": model_type,
"vocab_size": vocab,
"hidden_size": hidden,
"num_layers": layers,
"has_mla": bool(kv_lora and kv_lora > 0),
"kv_lora_rank": kv_lora,
},
"base_matches": sorted(base_matches, key=lambda x: -x["score"]),
"modelatlas_similar": db_matches,
"self_catalogued": self_catalogued,
"lab_catalogued": lab_catalogued,
}
def generate_verdict(
model_id: str,
config: dict,
metadata: dict,
claims: dict,
stage1: dict,
chat_template: str = "",
) -> dict:
"""Synthesize all signals into a human-readable verdict."""
now = datetime.now(timezone.utc).isoformat()
base_matches = stage1["base_matches"]
suspicious = claims.get("suspicious_name_terms", [])
# Headline verdict
self_cat = stage1.get("self_catalogued")
lab_cat = stage1.get("lab_catalogued")
if base_matches:
top = base_matches[0]
if top["confidence"] == "HIGH":
architecture_verdict = f"CONFIRMED β€” architecture matches {top['name']}"
else:
architecture_verdict = f"LIKELY β€” architecture consistent with {top['name']}"
headline_confidence = top["confidence"]
elif self_cat:
mt = self_cat.get("model_type") or "?"
org = self_cat.get("org")
architecture_verdict = (
f"RECOGNIZED β€” original '{mt}' architecture"
+ (f" from {org}" if org else "")
+ ", catalogued in ModelAtlas (not a derivative of a tracked base model)"
)
headline_confidence = "ORIGINAL"
elif lab_cat and not suspicious:
# not a derivative, no foreign-brand claim, published by a known lab -> a new original
architecture_verdict = (
f"NEW β€” original architecture from {lab_cat['org']} (known lab, "
f"{lab_cat['model_count']} catalogued models); not yet in the reference"
)
headline_confidence = "NEW"
else:
architecture_verdict = "UNRECOGNIZED β€” architecture does not match any known base model"
headline_confidence = "NONE"
# Claim accuracy flags
flags = []
confirmed_base_name = base_matches[0]["name"] if base_matches else None
# (1) IMPOSSIBLE_WEIGHT_PROVENANCE β€” closed-weight brand named, but architecture is a
# known open base. No weights were ever released for these brands, so weight-level
# provenance is impossible (not merely unverifiable).
closed_hits = sorted({_CLOSED_WEIGHT_BRANDS[t] for t in suspicious if t in _CLOSED_WEIGHT_BRANDS})
for brand in closed_hits:
flags.append({
"type": "IMPOSSIBLE_WEIGHT_PROVENANCE",
"severity": "HIGH",
"term": brand,
"explanation": (
f"The name references {brand}, whose weights have NEVER been publicly released. "
f"Weight transfer, merging, or initialization from {brand} is therefore impossible"
+ (f" β€” the architecture is {confirmed_base_name}, accounting for the model." if confirmed_base_name else ".")
+ " The only possible link is training on generated outputs (distillation), which "
"leaves no weight-level trace, cannot be verified, and may violate the provider's terms."
),
})
# (2) PROVENANCE_SCRUBBING / IDENTITY_OVERRIDE β€” from the chat template (config-only).
scrub = detect_provenance_scrubbing(chat_template)
if scrub:
flags.append(scrub)
# (3) NAMING_MISATTRIBUTION β€” name foregrounds a brand that is NOT the real base, while
# the real base is absent from the name ("sells X, built on Y, hides Y").
name_l = model_id.split("/")[-1].lower()
if confirmed_base_name and closed_hits:
base_token = confirmed_base_name.split()[0].lower().replace("-", "") # e.g. "qwen3.5"
base_family = re.sub(r"[0-9.\-]", "", base_token) # e.g. "qwen"
if base_family and base_family not in name_l.replace("-", ""):
flags.append({
"type": "NAMING_MISATTRIBUTION",
"severity": "MEDIUM",
"explanation": (
f"The model name foregrounds {', '.join(closed_hits)} but the actual base is "
f"{confirmed_base_name} β€” and '{base_family}' does not appear in the name. "
"The packaging markets a brand the model has no architectural claim to, while "
"omitting the base it is actually built from."
),
})
# (4) NAME_MISMATCH β€” name implies one open base, architecture says another.
name_implied = claims.get("name_implies", [])
if name_implied and base_matches:
top = base_matches[0]
top_base = top.get("name") or top.get("base") or ""
# Match the name token against BOTH the base key ("ministral3") and the human label
# ("Mistral 3.x …") β€” case-insensitive. The label carries the family word the raw
# model_type key may not (e.g. name 'mistral' vs key 'ministral3'). Prevents false
# NAME_MISMATCH on honestly-labeled models.
haystack = f"{top.get('base','')} {top.get('name','')}".lower()
if haystack.strip() and not any(n.lower() in haystack or haystack in n.lower() for n in name_implied):
flags.append({
"type": "NAME_MISMATCH",
"severity": "MEDIUM",
"explanation": f"Model name implies {name_implied} but architecture suggests {top_base}. Possible mislabeling.",
})
# Risk band. An UNRECOGNIZED model with no flags is UNVERIFIED, not CLEAN β€” we couldn't
# identify it, so 0 deception flags is absence of evidence, not a clean bill of health.
_risk, _band = _risk_score(flags)
if headline_confidence == "NONE" and _risk == 0:
_band = "UNKNOWN"
return {
"model_id": model_id,
"scanned_at": now,
"verdict": {
"architecture": architecture_verdict,
"base_model_confirmed": (
base_matches[0]["name"] if base_matches
else (f"{self_cat.get('model_type')} (original architecture)" if self_cat
else (f"original / new (from {lab_cat['org']})" if lab_cat else "Unknown"))
),
"confidence": headline_confidence,
"risk_score": _risk,
"risk_band": _band,
"engines": _engine_panel(
flags,
("confirmed" if base_matches else "original" if self_cat
else "new" if (lab_cat and not suspicious) else "unknown"),
bool(chat_template)),
"flags": flags,
"flag_count": len(flags),
"stage": "Stage 1 (config-only β€” no weight download)",
},
"evidence": {
"config_signals": stage1["config_signals"],
"base_matches": stage1["base_matches"][:3],
"modelatlas_similar": stage1["modelatlas_similar"][:3],
"claimed_base": claims.get("explicit_base"),
"name_implies": name_implied,
},
"metadata": {
"downloads": metadata.get("downloads", 0),
"likes": metadata.get("likes", 0),
"license": metadata.get("license", ""),
"created_at": metadata.get("created_at", ""),
},
"note": (
"Stage 1 validates architecture from config.json only (~2KB). "
"Stage 2 weight analysis (requires model download) provides stronger confirmation. "
"Powered by ModelAtlas β€” modeldna.ai Β· a RadicalNotion product."
),
}
_GGUF_ARCH_MAP = {
"qwen35": "Qwen3.5", "qwen3_5": "Qwen3.5", "qwen3": "Qwen3", "qwen2": "Qwen2.5",
"qwen35moe": "Qwen3.5/3.6 MoE", "qwen3moe": "Qwen3 MoE", "qwen2moe": "Qwen2 MoE",
"llama": "Llama", "llama4": "Llama 4", "gemma": "Gemma", "gemma2": "Gemma", "gemma3": "Gemma",
"phi3": "Phi-3", "mistral": "Mistral", "deepseek2": "DeepSeek V2/V3",
}
def inspect_gguf(model_id: str) -> dict:
"""OPTIONAL ACTION (offer #1): inspect a GGUF's embedded metadata (header-only, no
download) and run the provenance detectors on the template baked into the file."""
now = datetime.now(timezone.utc).isoformat()
md = fetch_gguf_metadata(model_id)
if not md:
return {"model_id": model_id, "scanned_at": now,
"error": "Could not read GGUF metadata header (file may be gated/missing)."}
arch_raw = md.get("general.architecture", "")
arch = _GGUF_ARCH_MAP.get(arch_raw, arch_raw or "unknown")
chat_template = md.get("tokenizer.chat_template", "")
flags = []
# provenance scrub baked into the GGUF's template
scrub = detect_provenance_scrubbing(chat_template)
if scrub:
scrub = dict(scrub); scrub["source"] = "gguf embedded chat_template"
flags.append(scrub)
# closed-weight brand + naming misattribution from the name
name_l = model_id.split("/")[-1].lower()
closed = sorted({_CLOSED_WEIGHT_BRANDS[t] for t in _CLOSED_WEIGHT_BRANDS if
re.search(r"\b" + re.escape(t) + r"\b", name_l)})
for brand in closed:
flags.append({
"type": "IMPOSSIBLE_WEIGHT_PROVENANCE", "severity": "HIGH", "term": brand,
"explanation": f"Name references {brand}, whose weights were never released β€” "
f"weight provenance is impossible. GGUF architecture is '{arch}'.",
})
arch_family = re.sub(r"[0-9.\-]", "", arch.lower())
if closed and arch_family and arch_family not in name_l.replace("-", ""):
flags.append({
"type": "NAMING_MISATTRIBUTION", "severity": "MEDIUM",
"explanation": f"Name foregrounds {', '.join(closed)} but GGUF architecture is "
f"{arch} β€” '{arch_family}' is absent from the name.",
})
return {
"model_id": model_id, "scanned_at": now,
"format": "GGUF (quantized)", "gguf_file": md.get("_gguf_file"),
"verdict": {
"architecture": f"CONFIRMED β€” GGUF architecture '{arch_raw}' = {arch}" if arch_raw
else "UNRECOGNIZED",
"risk_score": _risk_score(flags)[0],
"risk_band": _risk_score(flags)[1],
"engines": _engine_panel(flags, "confirmed" if arch_raw else "unknown", bool(chat_template)),
"flags": flags, "flag_count": len(flags),
"stage": "Stage 1 (GGUF metadata β€” header only, no download)",
},
"evidence": {"gguf_general.name": md.get("general.name"),
"gguf_architecture": arch_raw,
"chat_template_present": bool(chat_template),
"quantization": md.get("general.quantization_version") or md.get("general.file_type")},
"note": ("Read from the GGUF header only β€” no weights downloaded. The chat template "
"and identity behavior travel INTO the quant, so a scrub in the source is "
"present here too. Powered by ModelAtlas β€” modeldna.ai."),
}
def scan(model_id: str) -> dict:
"""Full Stage 1 scan. Entry point."""
t0 = time.time()
# GGUF: no standard config.json β€” offer metadata inspection + source resolution.
# Detect by name OR by the repo actually containing .gguf files (many GGUF-only repos
# don't put "gguf" in the name, e.g. HauhauCS/Qwen3.6-...-Aggressive).
name_lower = model_id.lower()
metadata = fetch_model_metadata(model_id)
files = metadata.get("files", [])
repo_has_gguf = any(str(f).lower().endswith(".gguf") for f in files)
# A repo is a GGUF *distribution* only if it has NO standard config.json. Repos that ship
# a real model (config.json + safetensors) AND convenience .gguf files (e.g.
# mixedbread-ai/mxbai-embed-large-v1) must be scanned normally, not treated as a quant.
has_config = any(str(f) == "config.json" for f in files)
if ("gguf" in name_lower or repo_has_gguf) and not has_config:
gguf_md = fetch_gguf_metadata(model_id)
source = resolve_gguf_source(model_id, metadata, gguf_md)
offers = []
offers.append({
"action": "inspect_gguf", "model_id": model_id,
"label": "Inspect this GGUF's embedded metadata (architecture + chat template) β€” "
"header-only, no download",
})
if source:
offers.append({
"action": "scan_source", "model_id": source,
"label": f"Scan the full-weights source ({source}) β€” full Stage 1 + Stage 2",
})
arch_raw = gguf_md.get("general.architecture", "")
return {
"model_id": model_id,
"scanned_at": datetime.now(timezone.utc).isoformat(),
"format": "GGUF (quantized)",
"detected": (
f"This is a GGUF quantization"
+ (f" of an '{arch_raw}' architecture model" if arch_raw else "")
+ (f", derived from {source}" if source else "") + ". "
"A quant is the SAME weights at lower precision β€” provenance is identical to "
"the source. The embedded chat template (and any identity scrub) travels into the file."
),
"resolved_source": source,
"offers": offers,
"elapsed_s": round(time.time() - t0, 2),
"note": "Powered by ModelAtlas β€” modeldna.ai Β· a RadicalNotion product.",
}
config = fetch_config(model_id)
if not config:
return {
"model_id": model_id,
"error": "Could not fetch config.json β€” model may be private, gated, or not exist on HuggingFace.",
"scanned_at": datetime.now(timezone.utc).isoformat(),
}
claims = detect_claimed_base(model_id, config, metadata)
stage1 = stage1_screen(model_id, config)
chat_template = fetch_chat_template(model_id)
verdict = generate_verdict(model_id, config, metadata, claims, stage1, chat_template)
verdict["elapsed_s"] = round(time.time() - t0, 2)
return verdict
if __name__ == "__main__":
import sys
model_id = sys.argv[1] if len(sys.argv) > 1 else "Qwen/Qwen3.5-27B"
result = scan(model_id)
print(json.dumps(result, indent=2, default=str))