Spaces:
Running
Running
Update model inspector
Browse files- Makefile +21 -0
- README.md +0 -10
- pyproject.toml +12 -0
- requirements.txt +5 -0
- scripts/create_venv.sh +13 -0
- scripts/install_uv.sh +19 -0
- scripts/setup_ssl.py +97 -0
- src/__init__.py +0 -0
- src/__pycache__/__init__.cpython-312.pyc +0 -0
- src/app.py +609 -0
- src/llm_architect.egg-info/PKG-INFO +4 -0
- src/llm_architect.egg-info/SOURCES.txt +6 -0
- src/llm_architect.egg-info/dependency_links.txt +1 -0
- src/llm_architect.egg-info/top_level.txt +1 -0
Makefile
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.PHONY: setup install clean lint test format dev
|
2 |
+
|
3 |
+
setup:
|
4 |
+
@echo "Setting up local environment..."
|
5 |
+
@../scripts/install_uv.sh
|
6 |
+
@uv python install 3.11
|
7 |
+
@../scripts/create_venv.sh
|
8 |
+
@. .venv/bin/activate && make install
|
9 |
+
|
10 |
+
install:
|
11 |
+
@echo "Installing dependencies..."
|
12 |
+
uv pip install -e .
|
13 |
+
|
14 |
+
clean:
|
15 |
+
@echo "Cleaning up..."
|
16 |
+
rm -rf .venv
|
17 |
+
rm -rf dist
|
18 |
+
rm -rf *.egg-info
|
19 |
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
20 |
+
find . -type d -name .pytest_cache -exec rm -rf {} +
|
21 |
+
find . -type d -name .ipynb_checkpoints -exec rm -rf {} +
|
README.md
CHANGED
@@ -1,14 +1,4 @@
|
|
1 |
---
|
2 |
-
title: Model Inspector
|
3 |
-
emoji: π»
|
4 |
-
colorFrom: green
|
5 |
-
colorTo: pink
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 5.41.0
|
8 |
-
app_file: app.py
|
9 |
pinned: false
|
10 |
-
license: mit
|
11 |
-
short_description: 'Interactive tool to compare any LLM architecture '
|
12 |
---
|
13 |
-
|
14 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
|
|
|
|
|
|
|
|
2 |
sdk: gradio
|
|
|
|
|
3 |
pinned: false
|
|
|
|
|
4 |
---
|
|
|
|
pyproject.toml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "llm_architect"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = ""
|
5 |
+
requires-python = ">=3.11"
|
6 |
+
|
7 |
+
[tool.setuptools.dynamic]
|
8 |
+
dependencies = {file = ["requirements.txt"]}
|
9 |
+
|
10 |
+
[tool.setuptools.packages.find]
|
11 |
+
where = ["src"]
|
12 |
+
namespaces = false
|
requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
transformers==4.55.0
|
2 |
+
gradio==5.41.0
|
3 |
+
gradio-client==1.11.0
|
4 |
+
ipython==9.4.0
|
5 |
+
plotly==6.2.0
|
scripts/create_venv.sh
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
# Exit on any error
|
4 |
+
set -e
|
5 |
+
|
6 |
+
# 3. Create a virtual environment if not already created
|
7 |
+
VENV_DIR=".venv"
|
8 |
+
if [ ! -d "$VENV_DIR" ]; then
|
9 |
+
echo "Virtual environment not found. Creating a new virtual environment..."
|
10 |
+
uv venv --python 3.11
|
11 |
+
else
|
12 |
+
echo "Virtual environment already exists."
|
13 |
+
fi
|
scripts/install_uv.sh
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
# Exit on any error
|
4 |
+
set -e
|
5 |
+
|
6 |
+
# Function to check if a command exists
|
7 |
+
command_exists() {
|
8 |
+
command -v "$1" >/dev/null 2>&1
|
9 |
+
}
|
10 |
+
|
11 |
+
# 1. Check if 'uv' is installed
|
12 |
+
if ! command_exists uv; then
|
13 |
+
echo "'uv' is not installed. Installing 'uv'..."
|
14 |
+
# Install 'uv'
|
15 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
16 |
+
source ~/.cargo/env
|
17 |
+
else
|
18 |
+
echo "'uv' is already installed."
|
19 |
+
fi
|
scripts/setup_ssl.py
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import subprocess
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
|
5 |
+
def check_openssl():
|
6 |
+
"""Check if OpenSSL is available."""
|
7 |
+
try:
|
8 |
+
subprocess.run(["openssl", "version"], capture_output=True, check=True)
|
9 |
+
return True
|
10 |
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
11 |
+
return False
|
12 |
+
|
13 |
+
|
14 |
+
def generate_ssl_certificates():
|
15 |
+
"""Generate self-signed SSL certificates for local development."""
|
16 |
+
cert_dir = Path("")
|
17 |
+
key_file = cert_dir / "key.pem"
|
18 |
+
cert_file = cert_dir / "cert.pem"
|
19 |
+
|
20 |
+
# Check if certificates already exist
|
21 |
+
if key_file.exists() and cert_file.exists():
|
22 |
+
print("β
SSL certificates already exist!")
|
23 |
+
return True
|
24 |
+
|
25 |
+
if not check_openssl():
|
26 |
+
print("β OpenSSL not found. Please install OpenSSL first.")
|
27 |
+
print("On Ubuntu/Debian: sudo apt-get install openssl")
|
28 |
+
print("On macOS: brew install openssl")
|
29 |
+
print(
|
30 |
+
"On Windows: Download from https://slproweb.com/products/Win32OpenSSL.html"
|
31 |
+
)
|
32 |
+
return False
|
33 |
+
|
34 |
+
print("π Generating self-signed SSL certificates...")
|
35 |
+
|
36 |
+
# Generate private key
|
37 |
+
key_cmd = ["openssl", "genrsa", "-out", str(key_file), "2048"]
|
38 |
+
|
39 |
+
# Generate certificate
|
40 |
+
cert_cmd = [
|
41 |
+
"openssl",
|
42 |
+
"req",
|
43 |
+
"-new",
|
44 |
+
"-x509",
|
45 |
+
"-key",
|
46 |
+
str(key_file),
|
47 |
+
"-out",
|
48 |
+
str(cert_file),
|
49 |
+
"-days",
|
50 |
+
"365",
|
51 |
+
"-subj",
|
52 |
+
"/C=US/ST=CA/L=Local/O=Dev/CN=localhost",
|
53 |
+
]
|
54 |
+
|
55 |
+
try:
|
56 |
+
subprocess.run(key_cmd, check=True)
|
57 |
+
subprocess.run(cert_cmd, check=True)
|
58 |
+
print("β
SSL certificates generated successfully!")
|
59 |
+
print(f" Private key: {key_file}")
|
60 |
+
print(f" Certificate: {cert_file}")
|
61 |
+
return True
|
62 |
+
except subprocess.CalledProcessError as e:
|
63 |
+
print(f"β Failed to generate SSL certificates: {e}")
|
64 |
+
return False
|
65 |
+
|
66 |
+
|
67 |
+
def main():
|
68 |
+
"""Main function to set up SSL certificates."""
|
69 |
+
print("π Setting up SSL certificates for local HTTPS...")
|
70 |
+
|
71 |
+
if generate_ssl_certificates():
|
72 |
+
print("\nπ Next steps:")
|
73 |
+
print("1. Run your Gradio app with the generated certificates")
|
74 |
+
print("2. Open https://localhost:7860 in your browser")
|
75 |
+
print(
|
76 |
+
"3. Accept the security warning (click 'Advanced' β 'Proceed to localhost')"
|
77 |
+
)
|
78 |
+
print("4. Allow microphone access when prompted")
|
79 |
+
print(
|
80 |
+
"\nβ οΈ Note: You'll see a security warning because these are self-signed certificates."
|
81 |
+
)
|
82 |
+
print(
|
83 |
+
" This is normal for local development - just click through the warning."
|
84 |
+
)
|
85 |
+
else:
|
86 |
+
print("\nβ SSL setup failed. Alternative options:")
|
87 |
+
print(
|
88 |
+
"1. Use Chrome with: --unsafely-treat-insecure-origin-as-secure=http://localhost:7860"
|
89 |
+
)
|
90 |
+
print(
|
91 |
+
"2. Use Firefox and set media.devices.insecure.enabled=true in about:config"
|
92 |
+
)
|
93 |
+
print("3. Deploy to a server with proper SSL certificates")
|
94 |
+
|
95 |
+
|
96 |
+
if __name__ == "__main__":
|
97 |
+
main()
|
src/__init__.py
ADDED
File without changes
|
src/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (194 Bytes). View file
|
|
src/app.py
ADDED
@@ -0,0 +1,609 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from typing import Dict, Any, Optional, List, Tuple, Union
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
from plotly.subplots import make_subplots
|
5 |
+
from transformers import AutoConfig
|
6 |
+
import logging
|
7 |
+
|
8 |
+
# Set up logging
|
9 |
+
logging.basicConfig(level=logging.INFO)
|
10 |
+
logger = logging.getLogger(__name__)
|
11 |
+
|
12 |
+
|
13 |
+
class PlotlyModelArchitectureVisualizer:
|
14 |
+
def __init__(self, hf_token: Optional[str] = None):
|
15 |
+
self.config = None
|
16 |
+
self.hf_token = hf_token
|
17 |
+
|
18 |
+
# Universal color scheme - consistent across all models
|
19 |
+
self.universal_colors = {
|
20 |
+
'embedding': '#f8f9fa', # Light gray for embeddings
|
21 |
+
'layer_norm': '#e9ecef', # Light gray for layer norms
|
22 |
+
'attention': '#495057', # Dark gray for attention
|
23 |
+
'output': '#f8f9fa', # Light gray for output layers
|
24 |
+
'text': '#212529', # Dark text
|
25 |
+
'container_outer': '#dee2e6', # Outer container
|
26 |
+
'moe_inner': '#d4edda', # Green background for MoE models
|
27 |
+
'dense_inner': '#f8d7da', # Red background for dense models
|
28 |
+
'feedforward_moe': '#28a745', # Green for MoE FFN
|
29 |
+
'feedforward_dense': '#dc3545', # Red for dense FFN
|
30 |
+
'router': '#fd7e14', # Orange for router
|
31 |
+
'expert': '#20c997', # Teal for experts
|
32 |
+
'callout_bg': 'rgba(255,255,255,0.9)',
|
33 |
+
'accent_blue': '#007bff',
|
34 |
+
'accent_green': '#28a745',
|
35 |
+
'accent_red': '#dc3545'
|
36 |
+
}
|
37 |
+
|
38 |
+
def get_model_config(self, model_name: str) -> Dict[str, Any]:
|
39 |
+
"""Fetch model configuration from Hugging Face"""
|
40 |
+
try:
|
41 |
+
logger.info(f"Fetching config for {model_name}")
|
42 |
+
config = AutoConfig.from_pretrained(model_name, token=self.hf_token, trust_remote_code=True)
|
43 |
+
return config.to_dict()
|
44 |
+
except Exception as e:
|
45 |
+
logger.error(f"Error fetching config for {model_name}: {e}")
|
46 |
+
return {}
|
47 |
+
|
48 |
+
def extract_config_values(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
49 |
+
"""Extract and normalize configuration values with architecture detection"""
|
50 |
+
|
51 |
+
# Detect model architecture type
|
52 |
+
model_type = config.get('model_type', 'unknown').lower()
|
53 |
+
is_moe = any(key in config for key in [
|
54 |
+
'num_experts', 'n_routed_experts', 'moe_intermediate_size',
|
55 |
+
'num_experts_per_tok', 'router_aux_loss_coef'
|
56 |
+
])
|
57 |
+
|
58 |
+
# Extract MoE-specific parameters
|
59 |
+
moe_params = {}
|
60 |
+
if is_moe:
|
61 |
+
moe_params = {
|
62 |
+
'num_experts': config.get('num_experts', config.get('n_routed_experts', 'N/A')),
|
63 |
+
'experts_per_token': config.get('num_experts_per_tok', 'N/A'),
|
64 |
+
'moe_intermediate_size': config.get('moe_intermediate_size', 'N/A'),
|
65 |
+
'router_aux_loss': config.get('router_aux_loss_coef', config.get('aux_loss_alpha', 'N/A')),
|
66 |
+
'shared_experts': config.get('n_shared_experts', 0)
|
67 |
+
}
|
68 |
+
|
69 |
+
# Calculate model size estimate (simplified)
|
70 |
+
hidden_size = config.get('hidden_size', config.get('d_model', config.get('n_embd', 0)))
|
71 |
+
num_layers = config.get('num_hidden_layers', config.get('n_layer', config.get('num_layers', 0)))
|
72 |
+
vocab_size = config.get('vocab_size', 0)
|
73 |
+
|
74 |
+
if isinstance(hidden_size, int) and isinstance(num_layers, int) and isinstance(vocab_size, int):
|
75 |
+
# Very rough parameter count estimation
|
76 |
+
if is_moe:
|
77 |
+
# MoE models are much larger but use fewer parameters per token
|
78 |
+
estimated_params = (hidden_size * num_layers * vocab_size) // 1000000 # Simplified
|
79 |
+
size_suffix = "B" if estimated_params > 1000 else "M"
|
80 |
+
estimated_params = estimated_params // 1000 if estimated_params > 1000 else estimated_params
|
81 |
+
else:
|
82 |
+
estimated_params = (hidden_size * num_layers * vocab_size) // 1000000
|
83 |
+
size_suffix = "B" if estimated_params > 1000 else "M"
|
84 |
+
estimated_params = estimated_params // 1000 if estimated_params > 1000 else estimated_params
|
85 |
+
else:
|
86 |
+
estimated_params = "Unknown"
|
87 |
+
size_suffix = ""
|
88 |
+
|
89 |
+
return {
|
90 |
+
'model_type': config.get('model_type', 'unknown'),
|
91 |
+
'hidden_size': hidden_size if hidden_size != 0 else 'N/A',
|
92 |
+
'num_layers': num_layers if num_layers != 0 else 'N/A',
|
93 |
+
'num_heads': config.get('num_attention_heads', config.get('n_head', config.get('num_heads', 'N/A'))),
|
94 |
+
'vocab_size': vocab_size if vocab_size != 0 else 'N/A',
|
95 |
+
'max_position': config.get('max_position_embeddings',
|
96 |
+
config.get('n_positions', config.get('max_seq_len', 'N/A'))),
|
97 |
+
'intermediate_size': config.get('intermediate_size',
|
98 |
+
config.get('d_ff', hidden_size if hidden_size != 0 else 'N/A')),
|
99 |
+
'is_moe': is_moe,
|
100 |
+
'moe_params': moe_params,
|
101 |
+
'estimated_size': f"{estimated_params}{size_suffix}" if estimated_params != "Unknown" else "Unknown",
|
102 |
+
'kv_heads': config.get('num_key_value_heads', config.get('num_heads', 'N/A')),
|
103 |
+
'head_dim': config.get('head_dim', config.get('qk_nope_head_dim', 'N/A')),
|
104 |
+
'activation': config.get('hidden_act', config.get('activation_function', 'N/A'))
|
105 |
+
}
|
106 |
+
|
107 |
+
def add_container(self, fig: go.Figure, x: float, y: float, width: float, height: float,
|
108 |
+
color: str, line_width: int = 1, row: int = 1, col: int = 1) -> None:
|
109 |
+
"""Add a container/background box"""
|
110 |
+
fig.add_shape(
|
111 |
+
type="rect",
|
112 |
+
x0=x, y0=y, x1=x + width, y1=y + height,
|
113 |
+
fillcolor=color,
|
114 |
+
line=dict(color='black', width=line_width),
|
115 |
+
layer="below",
|
116 |
+
row=row, col=col
|
117 |
+
)
|
118 |
+
|
119 |
+
def add_layer_box(self, fig: go.Figure, x: float, y: float, width: float, height: float,
|
120 |
+
text: str, color: str, hover_text: str = None, row: int = 1, col: int = 1,
|
121 |
+
text_size: int = 7) -> None:
|
122 |
+
"""Add a rounded rectangle representing a layer"""
|
123 |
+
|
124 |
+
# Add the box shape
|
125 |
+
fig.add_shape(
|
126 |
+
type="rect",
|
127 |
+
x0=x, y0=y, x1=x + width, y1=y + height,
|
128 |
+
fillcolor=color,
|
129 |
+
line=dict(color='black', width=1),
|
130 |
+
layer="below",
|
131 |
+
row=row, col=col
|
132 |
+
)
|
133 |
+
|
134 |
+
# Add text label
|
135 |
+
fig.add_annotation(
|
136 |
+
x=x + width / 2,
|
137 |
+
y=y + height / 2,
|
138 |
+
text=text,
|
139 |
+
showarrow=False,
|
140 |
+
font=dict(size=text_size, color=self.universal_colors['text']),
|
141 |
+
bgcolor=self.universal_colors['callout_bg'],
|
142 |
+
bordercolor="black",
|
143 |
+
borderwidth=1,
|
144 |
+
row=row, col=col
|
145 |
+
)
|
146 |
+
|
147 |
+
# Add invisible scatter point for hover functionality
|
148 |
+
if hover_text:
|
149 |
+
fig.add_trace(go.Scatter(
|
150 |
+
x=[x + width / 2],
|
151 |
+
y=[y + height / 2],
|
152 |
+
mode='markers',
|
153 |
+
marker=dict(size=12, opacity=0),
|
154 |
+
hovertemplate=f"<b>{text}</b><br>{hover_text}<extra></extra>",
|
155 |
+
showlegend=False,
|
156 |
+
name=text
|
157 |
+
), row=row, col=col)
|
158 |
+
|
159 |
+
def add_moe_router_visualization(self, fig: go.Figure, x: float, y: float,
|
160 |
+
config_values: Dict[str, Any], row: int = 1, col: int = 1) -> None:
|
161 |
+
"""Add MoE router and expert visualization with improved layout"""
|
162 |
+
|
163 |
+
moe_params = config_values['moe_params']
|
164 |
+
|
165 |
+
# Router box - positioned more centrally
|
166 |
+
router_width, router_height = 0.4, 0.12
|
167 |
+
router_x = x + 0.2 # Center it better within the available space
|
168 |
+
|
169 |
+
self.add_layer_box(
|
170 |
+
fig, router_x, y, router_width, router_height,
|
171 |
+
"Router", self.universal_colors['router'],
|
172 |
+
f"{moe_params['experts_per_token']} experts activated <br>from {moe_params['num_experts']} total",
|
173 |
+
row, col, 6
|
174 |
+
)
|
175 |
+
|
176 |
+
# Expert boxes - positioned with better spacing
|
177 |
+
expert_y = y - 0.25 # Closer to router
|
178 |
+
expert_width, expert_height = 0.18, 0.1
|
179 |
+
experts_to_show = min(3, int(moe_params['experts_per_token']) if isinstance(moe_params['experts_per_token'],
|
180 |
+
int) else 3)
|
181 |
+
|
182 |
+
# Center the experts under the router
|
183 |
+
total_expert_width = experts_to_show * expert_width + (experts_to_show - 1) * 0.04
|
184 |
+
experts_start_x = router_x + (router_width - total_expert_width) / 2
|
185 |
+
|
186 |
+
for i in range(experts_to_show):
|
187 |
+
expert_x = experts_start_x + i * (expert_width + 0.04)
|
188 |
+
self.add_layer_box(
|
189 |
+
fig, expert_x, expert_y, expert_width, expert_height,
|
190 |
+
f"Expert\n{i + 1}", self.universal_colors['expert'],
|
191 |
+
f"MoE intermediate size: {moe_params['moe_intermediate_size']}",
|
192 |
+
row, col, 5
|
193 |
+
)
|
194 |
+
|
195 |
+
# Arrow from router to expert - pointing downward
|
196 |
+
self.add_connection_arrow(
|
197 |
+
fig, router_x + router_width / 2, y,
|
198 |
+
expert_x + expert_width / 2, expert_y + expert_height, row, col
|
199 |
+
)
|
200 |
+
|
201 |
+
# Add "..." if more experts exist - positioned to the right
|
202 |
+
if experts_to_show < int(moe_params['experts_per_token']) if isinstance(moe_params['experts_per_token'],
|
203 |
+
int) else False:
|
204 |
+
fig.add_annotation(
|
205 |
+
x=experts_start_x + experts_to_show * (expert_width + 0.04) + 0.05,
|
206 |
+
y=expert_y + expert_height / 2,
|
207 |
+
text="...",
|
208 |
+
showarrow=False,
|
209 |
+
font=dict(size=8, color=self.universal_colors['text']),
|
210 |
+
row=row, col=col
|
211 |
+
)
|
212 |
+
|
213 |
+
def add_side_panel(self, fig: go.Figure, x: float, y: float, width: float, height: float,
|
214 |
+
title: str, components: List[str], config_values: Dict[str, Any],
|
215 |
+
row: int = 1, col: int = 1) -> None:
|
216 |
+
"""Add a side panel with component breakdown"""
|
217 |
+
|
218 |
+
# Panel container with dashed border
|
219 |
+
fig.add_shape(
|
220 |
+
type="rect",
|
221 |
+
x0=x, y0=y, x1=x + width, y1=y + height,
|
222 |
+
fillcolor=self.universal_colors['callout_bg'],
|
223 |
+
line=dict(color='gray', width=1, dash='dash'),
|
224 |
+
layer="below",
|
225 |
+
row=row, col=col
|
226 |
+
)
|
227 |
+
|
228 |
+
# Panel title
|
229 |
+
fig.add_annotation(
|
230 |
+
x=x + width / 2, y=y + height - 0.08,
|
231 |
+
text=f"<b>{title}</b>",
|
232 |
+
showarrow=False,
|
233 |
+
font=dict(size=8, color=self.universal_colors['text']),
|
234 |
+
row=row, col=col
|
235 |
+
)
|
236 |
+
|
237 |
+
# Component boxes
|
238 |
+
component_height = 0.1
|
239 |
+
start_y = y + height - 0.2
|
240 |
+
|
241 |
+
for i, component in enumerate(components):
|
242 |
+
comp_y = start_y - i * (component_height + 0.03)
|
243 |
+
|
244 |
+
if "Linear" in component:
|
245 |
+
color = self.universal_colors['output']
|
246 |
+
elif "activation" in component.lower() or "SiLU" in component or "ReLU" in component:
|
247 |
+
color = self.universal_colors['feedforward_moe'] if config_values['is_moe'] else self.universal_colors[
|
248 |
+
'feedforward_dense']
|
249 |
+
else:
|
250 |
+
color = self.universal_colors['embedding']
|
251 |
+
|
252 |
+
self.add_layer_box(
|
253 |
+
fig, x + 0.03, comp_y, width - 0.06, component_height,
|
254 |
+
component, color, None, row, col, 6
|
255 |
+
)
|
256 |
+
|
257 |
+
def add_connection_arrow(self, fig: go.Figure, start_x: float, start_y: float,
|
258 |
+
end_x: float, end_y: float, row: int = 1, col: int = 1) -> None:
|
259 |
+
"""Add an arrow between layers"""
|
260 |
+
fig.add_annotation(
|
261 |
+
x=end_x, y=end_y,
|
262 |
+
ax=start_x, ay=start_y,
|
263 |
+
xref=f'x{col}' if col > 1 else 'x',
|
264 |
+
yref=f'y{row}' if row > 1 else 'y',
|
265 |
+
axref=f'x{col}' if col > 1 else 'x',
|
266 |
+
ayref=f'y{row}' if row > 1 else 'y',
|
267 |
+
showarrow=True,
|
268 |
+
arrowhead=2,
|
269 |
+
arrowsize=1,
|
270 |
+
arrowwidth=1.5,
|
271 |
+
arrowcolor='black'
|
272 |
+
)
|
273 |
+
|
274 |
+
def create_single_model_diagram(self, fig: go.Figure, model_name: str,
|
275 |
+
config_values: Dict[str, Any], row: int = 1, col: int = 1) -> None:
|
276 |
+
"""Add a single model's architecture to the subplot with improved layout"""
|
277 |
+
|
278 |
+
# Layout parameters
|
279 |
+
base_x, base_y = 0.3, 0.2
|
280 |
+
main_width, main_height = 2.2, 2.8
|
281 |
+
layer_width, layer_height = 1.8, 0.2
|
282 |
+
|
283 |
+
# Model title with size
|
284 |
+
model_display_name = model_name.split('/')[-1] if '/' in model_name else model_name
|
285 |
+
title_text = f"<b>{model_display_name}</b>"
|
286 |
+
if config_values['estimated_size'] != "Unknown":
|
287 |
+
title_text += f" ({config_values['estimated_size']})"
|
288 |
+
|
289 |
+
fig.add_annotation(
|
290 |
+
x=base_x + main_width / 2, y=base_y + main_height + 0.2,
|
291 |
+
text=title_text,
|
292 |
+
showarrow=False,
|
293 |
+
font=dict(size=10, color=self.universal_colors['accent_blue']),
|
294 |
+
row=row, col=col
|
295 |
+
)
|
296 |
+
|
297 |
+
# Outer container (main frame)
|
298 |
+
self.add_container(
|
299 |
+
fig, base_x - 0.1, base_y - 0.1, main_width + 0.2, main_height + 0.2,
|
300 |
+
self.universal_colors['container_outer'], 2, row, col
|
301 |
+
)
|
302 |
+
|
303 |
+
# Inner container (colored by architecture type)
|
304 |
+
inner_color = (self.universal_colors['moe_inner'] if config_values['is_moe']
|
305 |
+
else self.universal_colors['dense_inner'])
|
306 |
+
self.add_container(
|
307 |
+
fig, base_x + 0.1, base_y + 0.8, main_width - 0.2, main_height - 1.2,
|
308 |
+
inner_color, 1, row, col
|
309 |
+
)
|
310 |
+
|
311 |
+
# Layer definitions with universal colors
|
312 |
+
layers = [
|
313 |
+
('Token Embedding', base_y + 0.3, self.universal_colors['embedding'],
|
314 |
+
f"Vocab: {config_values['vocab_size']:,}<br>Embedding dim: {config_values['hidden_size']}"),
|
315 |
+
('Layer Norm', base_y + 0.6, self.universal_colors['layer_norm'],
|
316 |
+
'Input normalization'),
|
317 |
+
(f'Multi-Head Attention\n({config_values["num_heads"]} heads)',
|
318 |
+
base_y + 0.9, self.universal_colors['attention'],
|
319 |
+
f"Heads: {config_values['num_heads']}<br>Hidden: {config_values['hidden_size']}<br>KV Heads: {config_values['kv_heads']}"),
|
320 |
+
('Layer Norm', base_y + 1.2, self.universal_colors['layer_norm'],
|
321 |
+
'Post-attention norm'),
|
322 |
+
]
|
323 |
+
|
324 |
+
# Add MoE or Dense FFN layer
|
325 |
+
if config_values['is_moe']:
|
326 |
+
layers.append((
|
327 |
+
'MoE Feed Forward',
|
328 |
+
base_y + 1.5, self.universal_colors['feedforward_moe'],
|
329 |
+
f"Experts: {config_values['moe_params']['num_experts']}<br>Active per token: {config_values['moe_params']['experts_per_token']}<br>MoE intermediate: {config_values['moe_params']['moe_intermediate_size']}"
|
330 |
+
))
|
331 |
+
else:
|
332 |
+
layers.append((
|
333 |
+
'Feed Forward Network',
|
334 |
+
base_y + 1.5, self.universal_colors['feedforward_dense'],
|
335 |
+
f"Intermediate size: {config_values['intermediate_size']}<br>Activation: {config_values['activation']}"
|
336 |
+
))
|
337 |
+
|
338 |
+
layers.extend([
|
339 |
+
('Layer Norm', base_y + 1.8, self.universal_colors['layer_norm'],
|
340 |
+
'Post-FFN normalization'),
|
341 |
+
('Output Projection', base_y + 2.1, self.universal_colors['output'],
|
342 |
+
f"Projects to vocab: {config_values['vocab_size']:,}")
|
343 |
+
])
|
344 |
+
|
345 |
+
# Add all layers
|
346 |
+
layer_centers = []
|
347 |
+
for layer_name, y_pos, color, hover_info in layers:
|
348 |
+
layer_x = base_x + (main_width - layer_width) / 2
|
349 |
+
|
350 |
+
self.add_layer_box(
|
351 |
+
fig, layer_x, y_pos, layer_width, layer_height,
|
352 |
+
layer_name, color, hover_info, row, col
|
353 |
+
)
|
354 |
+
|
355 |
+
layer_centers.append((layer_x + layer_width / 2, y_pos + layer_height / 2))
|
356 |
+
|
357 |
+
# Add arrows between layers
|
358 |
+
for i in range(len(layer_centers) - 1):
|
359 |
+
start_x, start_y = layer_centers[i]
|
360 |
+
end_x, end_y = layer_centers[i + 1]
|
361 |
+
|
362 |
+
arrow_start_y = start_y + layer_height / 2
|
363 |
+
arrow_end_y = end_y - layer_height / 2
|
364 |
+
|
365 |
+
if arrow_end_y > arrow_start_y:
|
366 |
+
self.add_connection_arrow(fig, start_x, arrow_start_y, end_x, arrow_end_y, row, col)
|
367 |
+
|
368 |
+
# Add layer repetition indicator
|
369 |
+
if isinstance(config_values['num_layers'], int) and config_values['num_layers'] > 1:
|
370 |
+
fig.add_annotation(
|
371 |
+
x=base_x - 0.05, y=base_y + 1.4,
|
372 |
+
text=f"Γ{config_values['num_layers']}<br>layers",
|
373 |
+
showarrow=False,
|
374 |
+
font=dict(size=7, color=self.universal_colors['text']),
|
375 |
+
bgcolor=self.universal_colors['callout_bg'],
|
376 |
+
bordercolor="black", borderwidth=1,
|
377 |
+
row=row, col=col
|
378 |
+
)
|
379 |
+
|
380 |
+
# Add side panel for component details
|
381 |
+
panel_x = base_x + main_width + 0.3
|
382 |
+
panel_y = base_y + 1.5 # Moved up to avoid MoE visualization
|
383 |
+
panel_width = 0.7
|
384 |
+
panel_height = 0.8
|
385 |
+
|
386 |
+
if config_values['is_moe']:
|
387 |
+
# MoE side panel
|
388 |
+
components = [
|
389 |
+
"Linear layer",
|
390 |
+
f"{config_values['activation'].upper()} activation",
|
391 |
+
"Linear layer",
|
392 |
+
"Router",
|
393 |
+
f"{config_values['moe_params']['experts_per_token']} active experts"
|
394 |
+
]
|
395 |
+
panel_title = "MoE Module"
|
396 |
+
else:
|
397 |
+
# Dense FFN side panel
|
398 |
+
components = [
|
399 |
+
"Linear layer",
|
400 |
+
f"{config_values['activation'].upper()} activation",
|
401 |
+
"Linear layer"
|
402 |
+
]
|
403 |
+
panel_title = "FeedForward Module"
|
404 |
+
|
405 |
+
self.add_side_panel(fig, panel_x, panel_y, panel_width, panel_height,
|
406 |
+
panel_title, components, config_values, row, col)
|
407 |
+
|
408 |
+
# Add MoE router visualization if applicable
|
409 |
+
if config_values['is_moe']:
|
410 |
+
# Position router visualization below side panel with better spacing
|
411 |
+
router_x = panel_x + 0.05
|
412 |
+
router_y = panel_y - 0.5
|
413 |
+
self.add_moe_router_visualization(fig, router_x, router_y, config_values, row, col)
|
414 |
+
|
415 |
+
def add_callout(self, fig: go.Figure, point_x: float, point_y: float,
|
416 |
+
text_x: float, text_y: float, text: str, row: int = 1, col: int = 1) -> None:
|
417 |
+
"""Add a callout with leader line - arrow points FROM point TO text"""
|
418 |
+
fig.add_annotation(
|
419 |
+
x=text_x, y=text_y, # Text position
|
420 |
+
ax=point_x, ay=point_y, # Arrow start position (the component being referenced)
|
421 |
+
text=text,
|
422 |
+
showarrow=True,
|
423 |
+
arrowhead=2, arrowsize=1, arrowwidth=1,
|
424 |
+
arrowcolor='gray',
|
425 |
+
font=dict(size=7),
|
426 |
+
bgcolor=self.universal_colors['callout_bg'],
|
427 |
+
bordercolor="gray", borderwidth=1,
|
428 |
+
xref=f'x{col}' if col > 1 else 'x',
|
429 |
+
yref=f'y{row}' if row > 1 else 'y',
|
430 |
+
axref=f'x{col}' if col > 1 else 'x',
|
431 |
+
ayref=f'y{row}' if row > 1 else 'y'
|
432 |
+
)
|
433 |
+
|
434 |
+
def create_comparison_diagram(self, models_data: List[Tuple[str, Dict[str, Any]]]) -> go.Figure:
|
435 |
+
"""Create comparison diagram for multiple models"""
|
436 |
+
|
437 |
+
num_models = len(models_data)
|
438 |
+
if num_models == 0:
|
439 |
+
return go.Figure()
|
440 |
+
|
441 |
+
# Create subplots - always use single row layout
|
442 |
+
if num_models == 1:
|
443 |
+
fig = make_subplots(rows=1, cols=1, subplot_titles=[models_data[0][0]])
|
444 |
+
elif num_models == 2:
|
445 |
+
fig = make_subplots(rows=1, cols=2,
|
446 |
+
subplot_titles=[model[0] for model in models_data])
|
447 |
+
else: # 3 models
|
448 |
+
fig = make_subplots(rows=1, cols=3,
|
449 |
+
subplot_titles=[model[0] for model in models_data])
|
450 |
+
|
451 |
+
# Set up layout
|
452 |
+
fig.update_layout(
|
453 |
+
height=700,
|
454 |
+
width=1200,
|
455 |
+
showlegend=False,
|
456 |
+
title_text="π§ Model Architecture Comparison",
|
457 |
+
title_x=0.5,
|
458 |
+
title_font=dict(size=18)
|
459 |
+
)
|
460 |
+
|
461 |
+
# Add each model to its subplot
|
462 |
+
for i, (model_name, config_values) in enumerate(models_data):
|
463 |
+
row, col = 1, i + 1
|
464 |
+
self.create_single_model_diagram(fig, model_name, config_values, row, col)
|
465 |
+
|
466 |
+
# Update axes to hide ticks and labels - expanded range for callouts
|
467 |
+
fig.update_xaxes(showgrid=False, showticklabels=False, zeroline=False, range=[0, 5.0])
|
468 |
+
fig.update_yaxes(showgrid=False, showticklabels=False, zeroline=False, range=[-0.5, 3.5])
|
469 |
+
|
470 |
+
return fig
|
471 |
+
|
472 |
+
def generate_visualization(self, model_names: List[str]) -> Union[go.Figure, str]:
|
473 |
+
"""Generate visualization for given models"""
|
474 |
+
|
475 |
+
# Filter out empty model names
|
476 |
+
valid_models = [name.strip() for name in model_names if name and name.strip()]
|
477 |
+
|
478 |
+
if not valid_models:
|
479 |
+
return "Please enter at least one model name."
|
480 |
+
|
481 |
+
models_data = []
|
482 |
+
errors = []
|
483 |
+
|
484 |
+
for model_name in valid_models:
|
485 |
+
try:
|
486 |
+
config = self.get_model_config(model_name)
|
487 |
+
if config:
|
488 |
+
config_values = self.extract_config_values(config)
|
489 |
+
models_data.append((model_name, config_values))
|
490 |
+
else:
|
491 |
+
errors.append(f"Could not load config for {model_name}")
|
492 |
+
except Exception as e:
|
493 |
+
errors.append(f"Error with {model_name}: {str(e)}")
|
494 |
+
|
495 |
+
if not models_data:
|
496 |
+
return f"β Could not load any models. Errors: {'; '.join(errors)}"
|
497 |
+
|
498 |
+
if errors:
|
499 |
+
logger.warning(f"Some models failed to load: {errors}")
|
500 |
+
|
501 |
+
try:
|
502 |
+
fig = self.create_comparison_diagram(models_data)
|
503 |
+
return fig
|
504 |
+
except Exception as e:
|
505 |
+
return f"β Error generating diagram: {str(e)}"
|
506 |
+
|
507 |
+
|
508 |
+
def create_gradio_interface():
|
509 |
+
"""Create and configure the Gradio interface"""
|
510 |
+
|
511 |
+
visualizer = PlotlyModelArchitectureVisualizer()
|
512 |
+
|
513 |
+
def process_models(model1: str, model2: str = "", model3: str = "") -> Union[go.Figure, str]:
|
514 |
+
"""Process the model inputs and generate visualization"""
|
515 |
+
models = [model1, model2, model3]
|
516 |
+
return visualizer.generate_visualization(models)
|
517 |
+
|
518 |
+
# Create the interface
|
519 |
+
with gr.Blocks(
|
520 |
+
title="π§ Model Architecture Visualizer",
|
521 |
+
theme=gr.themes.Soft(),
|
522 |
+
css="""
|
523 |
+
.gradio-container {
|
524 |
+
max-width: 1200px !important;
|
525 |
+
}
|
526 |
+
.model-input {
|
527 |
+
font-family: monospace;
|
528 |
+
}
|
529 |
+
"""
|
530 |
+
) as demo:
|
531 |
+
gr.Markdown("""
|
532 |
+
# π§ Interactive Model Architecture Visualizer
|
533 |
+
|
534 |
+
Compare up to 3 Hugging Face transformer models side-by-side!
|
535 |
+
Enter model IDs below to see their architecture diagrams with interactive features.
|
536 |
+
|
537 |
+
### π How to Use
|
538 |
+
|
539 |
+
1. **Enter Model IDs**: Use Hugging Face model identifiers (e.g., `moonshotai/Kimi-K2-Base`, `openai/gpt-oss-120b`, `deepseek-ai/DeepSeek-R1-0528`)
|
540 |
+
2. **Compare Models**: Add up to 3 models to see them side-by-side
|
541 |
+
3. **Explore Interactively**: Hover over components to see detailed specifications
|
542 |
+
""")
|
543 |
+
|
544 |
+
# Model inputs in a single row
|
545 |
+
gr.Markdown("### π Model Configuration")
|
546 |
+
with gr.Row():
|
547 |
+
model1 = gr.Textbox(
|
548 |
+
label="Model 1 (Required)",
|
549 |
+
placeholder="e.g., openai/gpt-oss-120b",
|
550 |
+
value="openai/gpt-oss-120b",
|
551 |
+
elem_classes=["model-input"]
|
552 |
+
)
|
553 |
+
|
554 |
+
model2 = gr.Textbox(
|
555 |
+
label="Model 2 (Optional)",
|
556 |
+
placeholder="e.g., moonshotai/Kimi-K2-Base",
|
557 |
+
elem_classes=["model-input"]
|
558 |
+
)
|
559 |
+
|
560 |
+
model3 = gr.Textbox(
|
561 |
+
label="Model 3 (Optional)",
|
562 |
+
placeholder="e.g., deepseek-ai/DeepSeek-R1-0528",
|
563 |
+
elem_classes=["model-input"]
|
564 |
+
)
|
565 |
+
|
566 |
+
with gr.Row():
|
567 |
+
generate_btn = gr.Button("π Generate Visualization", variant="primary", size="lg")
|
568 |
+
clear_btn = gr.Button("ποΈ Clear", variant="secondary")
|
569 |
+
|
570 |
+
# Visualization output - full width
|
571 |
+
output_plot = gr.Plot(
|
572 |
+
label="π§ Architecture Visualization",
|
573 |
+
show_label=True
|
574 |
+
)
|
575 |
+
|
576 |
+
# Event handlers
|
577 |
+
generate_btn.click(
|
578 |
+
fn=process_models,
|
579 |
+
inputs=[model1, model2, model3],
|
580 |
+
outputs=output_plot
|
581 |
+
)
|
582 |
+
|
583 |
+
clear_btn.click(
|
584 |
+
fn=lambda: ("", "", "", None),
|
585 |
+
outputs=[model1, model2, model3, output_plot]
|
586 |
+
)
|
587 |
+
|
588 |
+
# Auto-generate for default model
|
589 |
+
demo.load(
|
590 |
+
fn=lambda: process_models("openai/gpt-oss-120b"),
|
591 |
+
outputs=output_plot
|
592 |
+
)
|
593 |
+
|
594 |
+
gr.Markdown("""Built with β€οΈ using Plotly, Gradio, and Hugging Face Transformers""")
|
595 |
+
|
596 |
+
return demo
|
597 |
+
|
598 |
+
|
599 |
+
if __name__ == "__main__":
|
600 |
+
# Create and launch the app
|
601 |
+
demo = create_gradio_interface()
|
602 |
+
|
603 |
+
# For HuggingFace Spaces deployment
|
604 |
+
demo.launch(
|
605 |
+
share=False,
|
606 |
+
server_name="0.0.0.0",
|
607 |
+
server_port=7860,
|
608 |
+
show_error=True
|
609 |
+
)
|
src/llm_architect.egg-info/PKG-INFO
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Metadata-Version: 2.4
|
2 |
+
Name: llm_architect
|
3 |
+
Version: 0.1.0
|
4 |
+
Requires-Python: >=3.11
|
src/llm_architect.egg-info/SOURCES.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
README.md
|
2 |
+
pyproject.toml
|
3 |
+
src/llm_architect.egg-info/PKG-INFO
|
4 |
+
src/llm_architect.egg-info/SOURCES.txt
|
5 |
+
src/llm_architect.egg-info/dependency_links.txt
|
6 |
+
src/llm_architect.egg-info/top_level.txt
|
src/llm_architect.egg-info/dependency_links.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
|
src/llm_architect.egg-info/top_level.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
|