Permissive
Noncommercial
Unknown
# ============================================================ # π³ Model Family Tree β Local Version # Single-window desktop app using pywebview (Edge/WebView2) # The tree renders inside the app β no external browser needed # ============================================================ import json import threading import requests import networkx as nx import webview # type: ignore (installed via requirements.txt) from collections import defaultdict from typing import Optional from huggingface_hub import ModelCard # ββ Configuration ββββββββββββββββββββββββββββββββββββββββββββ DEFAULT_MODEL_ID = "morikomorizz/GRM-2.6-Plus-Primal" # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ # ββ Cached model card loader ββββββββββββββββββββββββββββββββββ class CachedModelCard(ModelCard): _cache: dict = {} @classmethod def load(cls, model_id: str, **kwargs) -> "ModelCard": if model_id not in cls._cache: try: cls._cache[model_id] = super().load(model_id, **kwargs) except Exception: cls._cache[model_id] = None return cls._cache[model_id] @classmethod def clear_cache(cls): cls._cache.clear() # ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββ def _js_escape(s: str) -> str: """Escape a string for safe use inside a JS single-quoted string.""" return (s.replace("\\", "\\\\") .replace("'", "\\'") .replace("\n", " ") .replace("\r", "")) def get_model_names_from_yaml(url: str) -> list: try: r = requests.get(url, timeout=10) if r.status_code == 200: return [line for line in r.content.decode("utf-8", errors="ignore").splitlines() if "/" in line] except Exception: pass return [] def get_license_color(model: str) -> str: PERMISSIVE = {"mit", "bsd", "apache-2.0", "openrail"} try: card = CachedModelCard.load(model) if card is None: return "#d3d3d3" lic = card.data.to_dict().get("license", "").lower() return "#90ee90" if any(p in lic for p in PERMISSIVE) else "#f08080" except Exception: return "#d3d3d3" def get_model_names(model: str, genealogy: dict, found: Optional[set] = None, visited: Optional[set] = None, log=None) -> set: if found is None: found = set() if visited is None: visited = set() if model in visited: return found visited.add(model) if log: log(f"Visiting: {model}") try: card = CachedModelCard.load(model) if card is None: raise ValueError("Model not found or is private") d = card.data.to_dict() tags: list = [] if "base_model" in d: tags = d["base_model"] if "tags" in d and not tags: tags = [t for t in d["tags"] if "/" in str(t)] if not tags: tags = get_model_names_from_yaml( f"https://huggingface.co/{model}/blob/main/merge.yml") if not tags: tags = get_model_names_from_yaml( f"https://huggingface.co/{model}/blob/main/mergekit_config.yml") if not isinstance(tags, list): tags = [tags] if tags else [] found.add(model) if log: log(f" β {len(tags)} parent(s) found") for tag in tags: genealogy[tag].append(model) get_model_names(tag, genealogy, found, visited, log) except Exception as e: if log: log(f" β {e}") return found # ββ pywebview JS API ββββββββββββββββββββββββββββββββββββββββββ class Api: def __init__(self): self._window = None def set_window(self, win): self._window = win def _js(self, code: str): if self._window: self._window.evaluate_js(code) def _log(self, msg: str): self._js(f"appendLog('{_js_escape(msg)}')") def generate(self, model_id: str): """Called from JS when the user clicks Generate.""" model_id = model_id.strip() if not model_id: return threading.Thread(target=self._do_generate, args=(model_id,), daemon=True).start() def _do_generate(self, model_id: str): try: self._js("setStatus('running')") CachedModelCard.clear_cache() genealogy: dict = defaultdict(list) get_model_names(model_id, genealogy, log=self._log) G = nx.DiGraph() for parent, children in genealogy.items(): for child in children: G.add_edge(parent, child) if G.number_of_nodes() == 0: G.add_node(model_id) self._log("\nColouring nodes by licenseβ¦") nodes = [] for node in G.nodes(): nodes.append({ "id": node, "label": node.replace("/", "\n"), "title": node, "color": get_license_color(node), }) edges = [{"from": src, "to": dst} for src, dst in G.edges()] payload = json.dumps({"nodes": nodes, "edges": edges}) self._js(f"renderTree({payload})") self._log(f"\nβ Done β {len(nodes)} model(s) in tree.") self._js("setStatus('done')") except Exception as e: safe = _js_escape(str(e)) self._js(f"setStatus('error', '{safe}')") # ββ Embedded HTML / CSS / JS ββββββββββββββββββββββββββββββββββ HTML = r"""