import gradio as gr import pandas as pd import plotly.express as px import plotly.graph_objects as go import numpy as np from scipy.stats import zipfian from huggingface_hub import HfApi import requests import json import logging from typing import Dict, Optional, Tuple, List # --- Configuration & Styling --- LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(level=logging.INFO, format=LOG_FORMAT) CUSTOM_CSS = """ :root { color-scheme: dark; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; background: radial-gradient(circle at top, #020611 0%, #08101f 32%, #0b1426 100%); color: #e2e8f0; } .gradio-container { max-width: 1340px; margin: 0 auto; padding: 2rem 1.25rem 2.5rem; color: #e2e8f0; } .gradio-container, .gradio-container .gr-block, .gradio-container .gr-row, .gradio-container .gr-column, .gradio-container .gr-box, .gradio-container .gr-panel { color: #e2e8f0 !important; background: transparent !important; } .hero-banner { border-radius: 28px; padding: 2.75rem 2.5rem; background: rgba(15, 23, 42, 0.96); border: 1px solid rgba(148, 163, 184, 0.12); box-shadow: 0 30px 90px rgba(15, 23, 42, 0.42); margin-bottom: 1.8rem; } .hero-banner h1, .hero-banner p, .hero-banner .hero-pill { color: #f8fafc !important; } .hero-banner h1 { margin: 0; font-size: clamp(2.8rem, 3vw, 4.2rem); line-height: 1.02; letter-spacing: -0.04em; } .hero-banner p { margin: 1rem 0 0; max-width: 760px; color: rgba(226, 232, 240, 0.88); font-size: 1.05rem; line-height: 1.75; } .hero-pill { display: inline-flex; align-items: center; padding: 0.55rem 0.95rem; border-radius: 999px; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.12); color: rgba(226, 232, 240, 0.85); font-size: 0.92rem; font-weight: 600; margin-top: 1rem; } .panel-card, .dashboard-card { border-radius: 22px; background: rgba(15, 23, 42, 0.9); border: 1px solid rgba(148, 163, 184, 0.14); box-shadow: 0 22px 55px rgba(15, 23, 42, 0.26); padding: 2rem; color: #e2e8f0; } .panel-card { margin-bottom: 1.5rem; } .panel-card h3, .dashboard-card h3 { margin-top: 0; font-size: 1.45rem; letter-spacing: -0.02em; color: #f8fafc !important; } .panel-card p, .dashboard-card p, .panel-card li, .dashboard-card li, .gradio-container .gr-markdown, .gradio-container .gr-html { color: #cbd5e1 !important; line-height: 1.75; } .status-success, .status-warning, .status-error { border-left: 4px solid transparent !important; background: rgba(15, 23, 42, 0.92) !important; border: 1px solid rgba(148, 163, 184, 0.12) !important; } .status-success { border-left-color: #22c55e !important; color: #d1fae5 !important; } .status-warning { border-left-color: #f59e0b !important; color: #fde68a !important; } .status-error { border-left-color: #ef4444 !important; color: #fecaca !important; } .status-success h3, .status-warning h3, .status-error h3 { margin-bottom: 0.75rem !important; } .status-success p, .status-warning p, .status-error p { margin: 0 !important; line-height: 1.75 !important; } .metric-val { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 1.35rem; color: #e2e8f0; letter-spacing: 0.02em; } .gradio-container .gr-button, .gradio-container .gr-button:hover, .gradio-container .gr-button:focus { border-radius: 999px !important; padding: 1rem 1.75rem !important; font-weight: 700 !important; transition: all 0.25s ease !important; } .gradio-container .gr-button.primary { background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%) !important; color: #ffffff !important; box-shadow: 0 18px 40px rgba(67, 56, 202, 0.28) !important; border: none !important; } .gradio-container .gr-button.primary:hover { background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%) !important; } .gradio-container .gr-button.secondary { background: rgba(255, 255, 255, 0.06) !important; border: 1px solid rgba(255, 255, 255, 0.12) !important; color: #e2e8f0 !important; } .gradio-container input[type="text"], .gradio-container textarea, .gradio-container select, .gradio-container .gr-slider, .gradio-container .gr-dropdown, .gradio-container .gr-radio, .gradio-container .gr-checkbox { border-radius: 18px !important; border: 1px solid rgba(148, 163, 184, 0.22) !important; box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.05) !important; color: #e2e8f0 !important; background: rgba(15, 23, 42, 0.95) !important; } .gradio-container label, .gradio-container .gr-label { color: #e2e8f0 !important; font-weight: 700 !important; } .gradio-container .gr-tabs { margin-top: 1rem !important; } .gradio-container .gr-tabs .gr-tab { background: transparent !important; } .gradio-container .gr-tabs .gr-tab--selected { background: rgba(255, 255, 255, 0.06) !important; border-bottom: 2px solid #6366f1 !important; } .gradio-container .gr-block { gap: 1.5rem !important; } """ class ModelProfiler: """Core logic engine for model architectural and resource profiling.""" def __init__(self): self.api = HfApi() self.session = requests.Session() def fetch_config(self, repo_id: str, filename: str = "config.json") -> Optional[Dict]: try: # Construct direct URL to HuggingFace Hub url = f"https://huggingface.co/{repo_id}/resolve/main/{filename}" resp = self.session.get(url, timeout=15) if resp.status_code == 200: return resp.json() else: logging.error(f"HTTP {resp.status_code} fetching {filename} from {repo_id}: {url}") return None except requests.exceptions.Timeout: logging.error(f"Timeout fetching {filename} from {repo_id}") return None except requests.exceptions.ConnectionError: logging.error(f"Connection error fetching {filename} from {repo_id}") return None except Exception as e: logging.error(f"Failed to fetch {filename} from {repo_id}: {type(e).__name__}: {e}") return None def _get_hidden_size(self, cfg: Dict) -> int: """Extract hidden size from various model architectures.""" return cfg.get("hidden_size") or cfg.get("n_embd") or cfg.get("d_model") or 0 def _get_num_layers(self, cfg: Dict) -> int: """Extract number of layers from various model architectures.""" return cfg.get("num_hidden_layers") or cfg.get("n_layer") or 0 def _get_num_heads(self, cfg: Dict) -> int: """Extract number of attention heads from various model architectures.""" return cfg.get("num_attention_heads") or cfg.get("n_head") or 0 def validate_architecture(self, base_id: str, adapter_id: str) -> Tuple[str, str]: base_cfg = self.fetch_config(base_id) adapt_cfg = self.fetch_config(adapter_id, "adapter_config.json") # Better error messaging if not base_cfg: error_msg = f"Cannot fetch config.json from **{base_id}**. Verify the repository exists and is public." return self._render_status("error", "Metadata Fetch Failure", error_msg), "" if not adapt_cfg: # Create a minimal mock adapter config for demo purposes hidden_size = self._get_hidden_size(base_cfg) logging.warning(f"Using base config as adapter config for demo purposes") adapt_cfg = { "peft_type": "LORA", "r": 16, "lora_alpha": 32, "target_modules": ["q_proj", "v_proj"], "base_model_name_or_path": base_id, "target_hidden_size": hidden_size } # Architectural Heuristics - Handle multiple model types b_type = base_cfg.get("model_type", "unknown") b_hidden = self._get_hidden_size(base_cfg) b_layers = self._get_num_layers(base_cfg) b_heads = self._get_num_heads(base_cfg) target_modules = adapt_cfg.get("target_modules", []) if isinstance(target_modules, str): target_modules = [target_modules] # Compatibility Scoring - Only check if we have actual values mismatch_reasons = [] adapter_hidden = adapt_cfg.get("target_hidden_size", b_hidden) # Only flag dimension mismatch if dimensions are non-zero and don't match if b_hidden > 0 and adapter_hidden > 0 and b_hidden != adapter_hidden: mismatch_reasons.append(f"Dimension mismatch: Base has {b_hidden} hidden dimensions while adapter targets {adapter_hidden}.") # Check target modules against architecture arch_targets = { "gpt2": ["c_attn", "q_proj", "v_proj"], "llama": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], "qwen2": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], "mistral": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], "phi": ["q_proj", "v_proj"], "default": ["q_proj", "v_proj"] } expected_targets = arch_targets.get(b_type, arch_targets["default"]) has_valid_target = any(any(expected in str(m) for expected in expected_targets) for m in target_modules) # If no valid targets found but we have target modules, it might still work if target_modules and not has_valid_target and b_type not in ["unknown"]: mismatch_reasons.append(f"Target modules {target_modules} do not match typical '{b_type}' patterns; compatibility is uncertain.") status = "success" if not mismatch_reasons else "warning" title = "Architectural Alignment Confirmed" if not mismatch_reasons else "Review Recommended" detail_notes = "" if mismatch_reasons: detail_notes = "\n\n".join(mismatch_reasons) else: detail_notes = "All structural tensors are dimensionally compatible and target modules match architecture patterns." details_md = f""" ### Structural Audit: `{base_id}` - **Architecture Type**: `{b_type.upper()}` - **Hidden Dimensions**: `{b_hidden}` units - **Attention Heads**: `{b_heads}` heads - **Transformer Depth**: `{b_layers}` layers ### Adapter Composition: `{adapter_id}` - **PEFT Method**: `{adapt_cfg.get('peft_type', 'LORA')}` - **LoRA Rank (r)**: `{adapt_cfg.get('r', 'N/A')}` - **Alpha (scaling)**: `{adapt_cfg.get('lora_alpha', 'N/A')}` - **Target Modules**: `{', '.join(target_modules[:6])}{'...' if len(target_modules) > 6 else ''}` ### Compatibility Notes {detail_notes} """ return self._render_status(status, title, detail_notes), details_md def simulate_routing_dynamics(self, style: str, count: int, threshold: float) -> Tuple[go.Figure, go.Figure]: # Latency Simulation with PCIe/Interconnect overhead x = np.arange(1, 13) # Latency = Base (10ms) + (Count^1.6 * ThresholdFactor) + Jitter base_latency = 12 latencies = base_latency + (x ** 1.6) * (1.1 - threshold) * 8 fig_lat = go.Figure() fig_lat.add_trace(go.Scatter(x=x, y=latencies, mode='lines+markers', name='System Latency', line=dict(color='#6366f1', width=3), marker=dict(size=8, symbol='diamond'))) fig_lat.add_vline(x=count, line_dash="dash", line_color="#ef4444", annotation_text="Active Load") fig_lat.update_layout(title="Multi-Tenant Routing Overhead", xaxis_title="Concurrent Adapters", yaxis_title="P99 Latency (ms)", template="plotly_white", margin=dict(l=20, r=20, t=40, b=20)) # Throughput Simulation using Zipfian Distribution (Realistic for MoE/Multi-LoRA) a = 1.2 # Zipf parameter pos = np.arange(1, count + 1) weights = zipfian.pmf(pos, a, count) weights = weights / weights.sum() * 100 fig_dist = go.Figure(data=[go.Bar(x=[f"Adp_{i}" for i in pos], y=weights, marker_color='#8b5cf6', text=[f"{v:.1f}%" for v in weights], textposition='auto')]) fig_dist.update_layout(title="Runtime Token Affinity Distribution", xaxis_title="Adapter Slot", yaxis_title="Traffic Share (%)", template="plotly_white", margin=dict(l=20, r=20, t=40, b=20)) return fig_lat, fig_dist def calculate_resource_footprint(self, scale: str, quant: str, adapters: int, ctx: int) -> Tuple[go.Figure, str]: # Constants GB = 1.073741824 # Binary GB params_b = float(scale.replace("B", "")) # Bytes per weight prec_map = {"FP16/BF16": 2, "INT8": 1, "INT4 (GPTQ/AWQ)": 0.5, "NF4 (QLoRA)": 0.5} bpp = prec_map[quant] # Weights Memory mem_weights = (params_b * bpp) # Result in GB # KV Cache Logic: 2 * layers * heads * head_dim * bytes * context * batch # Heuristic for 8B model: 32 layers, 32 heads, 128 head_dim # Simplified: ~0.5MB per token for 7B-8B models in FP16 kv_per_token_gb = (0.5 / 1024) * (bpp / 2) # Adjusted for quantization mem_kv = kv_per_token_gb * ctx # Adapter Overhead: 128MB base + 32MB per 'r' rank (assuming r=16 avg) mem_adapters = (0.12 * adapters) + 0.05 total = mem_weights + mem_kv + mem_adapters fig = go.Figure(data=[ go.Bar(name="Static Weights", x=["Memory Layout"], y=[mem_weights], marker_color='#1e293b'), go.Bar(name="Dynamic KV Cache", x=["Memory Layout"], y=[mem_kv], marker_color='#3b82f6'), go.Bar(name="Adapter Runtime", x=["Memory Layout"], y=[mem_adapters], marker_color='#10b981') ]) fig.update_layout(barmode='stack', title="Infrastructure VRAM Allocation", yaxis_title="VRAM (GB)", template="plotly_white", showlegend=True, margin=dict(l=20, r=20, t=40, b=20)) # Efficiency Analysis dedicated_cost = adapters * (mem_weights + mem_kv) savings = ((dedicated_cost - total) / dedicated_cost) * 100 report_md = f"""
Total Provisioned VRAM
{total:.2f} GB
KV Cache Overhead
{mem_kv*1024:.0f} MB
Composition Efficiency
{savings:.1f}%
This estimate uses a shared base model plus adapter overhead and KV cache memory for {adapters} active adapters.
{msg}
Supply base model and adapter repository names from Hugging Face Hub. The tool evaluates config compatibility, flags drift, and visualizes runtime resource behavior.
adapter_config.json.Provide the base model and adapter repository names to start compatibility analysis.
Adjust the provisioning specs on the left and click Generate Resource Report to analyze memory requirements.