Molbap HF Staff commited on
Commit
47c27ab
·
verified ·
1 Parent(s): c0a0e96

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -917
app.py DELETED
@@ -1,917 +0,0 @@
1
- # Standard library imports
2
- import re
3
- import subprocess
4
- import threading
5
- import time
6
- from pathlib import Path
7
- from html import escape
8
- # Third-party imports
9
- import gradio as gr
10
- import numpy as np
11
- import pandas as pd
12
- import torch
13
- import spaces
14
- from transformers import AutoModelForCausalLM
15
- from transformers import modeling_utils as transformers_modeling
16
-
17
- # Optional imports for markdown processing
18
- try:
19
- from importlib import import_module
20
- from markdown_it import MarkdownIt
21
- HAS_MARKDOWN_IT = True
22
- except ImportError:
23
- HAS_MARKDOWN_IT = False
24
-
25
- try:
26
- import markdown
27
- HAS_PYTHON_MARKDOWN = True
28
- except ImportError:
29
- HAS_PYTHON_MARKDOWN = False
30
-
31
- try:
32
- from fastrtc import WebRTC, ReplyOnPause
33
- HAS_FASTRTC = True
34
- except ImportError:
35
- HAS_FASTRTC = False
36
-
37
- # ---------------------------
38
- # Markdown rendering
39
- # ---------------------------
40
-
41
- def _create_markdownit_renderer():
42
- """Create markdown-it renderer with plugins if available."""
43
- if not HAS_MARKDOWN_IT:
44
- return None
45
-
46
- try:
47
- markdown_parser = MarkdownIt("gfm-like")
48
-
49
- # Version-agnostic plugin loading
50
- footnote_module = import_module("mdit_py_plugins.footnote")
51
- footnote_plugin = getattr(footnote_module, "footnote", None) or getattr(footnote_module, "footnote_plugin")
52
- markdown_parser.use(footnote_plugin)
53
-
54
- tasklist_module = import_module("mdit_py_plugins.tasklists")
55
- tasklist_plugin = getattr(tasklist_module, "tasklists", None) or getattr(tasklist_module, "tasklists_plugin")
56
- markdown_parser.use(tasklist_plugin)
57
-
58
- container_module = import_module("mdit_py_plugins.container")
59
- container_plugin = getattr(container_module, "container", None) or getattr(container_module, "container_plugin")
60
- try:
61
- markdown_parser.use(container_plugin, "details")
62
- except TypeError:
63
- markdown_parser.use(lambda m: container_plugin(m, name="details"))
64
- return markdown_parser
65
- except Exception:
66
- return None
67
-
68
- def _create_python_markdown_config():
69
- """Create Python-Markdown configuration as fallback."""
70
- if not HAS_PYTHON_MARKDOWN:
71
- return None
72
-
73
- extensions = [
74
- "extra", # tables + fenced code
75
- "footnotes",
76
- "admonition",
77
- "toc",
78
- "pymdownx.details",
79
- "pymdownx.superfences",
80
- "pymdownx.tasklist",
81
- ]
82
- extension_config = {
83
- "pymdownx.tasklist": {"custom_checkbox": True},
84
- "toc": {"permalink": True}
85
- }
86
- return ("python-markdown", extensions, extension_config, markdown)
87
-
88
- # Initialize markdown engine
89
- markdown_renderer = _create_markdownit_renderer()
90
- if markdown_renderer:
91
- markdown_engine = ("markdown-it", markdown_renderer)
92
- else:
93
- markdown_engine = _create_python_markdown_config()
94
- if not markdown_engine:
95
- raise ImportError("No markdown processor available")
96
-
97
- def _obsidian_rewrites(text: str) -> str:
98
- # 1) Obsidian image embeds: ![[img.png]] -> ![](file=content/img.png)
99
- text = re.sub(r'!\[\[([^\]|]+)\]\]', r'![](file=content/\1)', text)
100
-
101
- # 2) Standard Markdown images with relative paths: ![alt](path.png) -> ![alt](file=path.png)
102
- # Skip if already http(s) or file=
103
- text = re.sub(
104
- r'!\[([^\]]*)\]\(((?!https?://|file=)[^)]+)\)',
105
- r'![\1](file=\2)',
106
- text,
107
- )
108
-
109
- # 3) Obsidian wiki links (non-image): [[file|label]] / [[file]]
110
- text = re.sub(r'\[\[([^\]|]+)\|([^\]]+)\]\]', r'[\2](\1)', text)
111
- text = re.sub(r'\[\[([^\]]+)\]\]', r'[\1](\1)', text)
112
-
113
- # 4) Encode spaces in file= URLs so the browser doesn’t choke
114
- def _enc(m):
115
- return "file=" + m.group(1).replace(" ", "%20")
116
- text = re.sub(r'file=([^)>\s]+)', _enc, text)
117
-
118
- return text
119
-
120
-
121
- def markdown_to_html(text: str) -> str:
122
- """Convert markdown text to HTML using the configured renderer."""
123
- text = _obsidian_rewrites(text)
124
-
125
- if markdown_engine[0] == "markdown-it":
126
- renderer = markdown_engine[1]
127
- return renderer.render(text)
128
- else:
129
- engine_type, extensions, extension_config, markdown_module = markdown_engine
130
- return markdown_module.markdown(
131
- text,
132
- extensions=extensions,
133
- extension_configs=extension_config,
134
- output_format="html5"
135
- )
136
-
137
- def render_article(article_path: str, component_inserts: dict[str, callable]):
138
- raw = Path(article_path).read_text(encoding="utf-8") if Path(article_path).exists() else f"**Missing article**: `{article_path}`."
139
- parts = re.split(r"\{\{([A-Z_]+)\}\}", raw)
140
- with gr.Column(elem_id="article-content"):
141
- for i, part in enumerate(parts):
142
- if i % 2 == 0:
143
- gr.HTML(f'<div class="article">{markdown_to_html(part)}</div>')
144
- else:
145
- (component_inserts.get(part) or (lambda: gr.HTML(f"<p><em>Unknown component: {part}</em></p>")))()
146
-
147
-
148
- # ---------------------------
149
- # Terminal (safe, simplified)
150
- # ---------------------------
151
-
152
- def run_shell(cmd: str) -> str:
153
- banned = ["|", ">", "<", "&&", "||", "`"]
154
- if any(b in cmd for b in banned):
155
- return "$ " + cmd + "\nBlocked characters. Use a single command."
156
- try:
157
- p = subprocess.run(cmd, shell=True, check=False, capture_output=True, text=True, timeout=30)
158
- return f"$ {cmd}\n{p.stdout}{p.stderr}"
159
- except Exception as e:
160
- return f"$ {cmd}\n{e!r}"
161
-
162
-
163
- def build_code_compare(left_path, right_path, left_title="modular", right_title="modeling"):
164
- def _build():
165
- left = Path(left_path).read_text(encoding="utf-8") if Path(left_path).exists() else "# Missing " + left_path
166
- right = Path(right_path).read_text(encoding="utf-8") if Path(right_path).exists() else "# Missing " + right_path
167
- gr.HTML(f"""
168
- <div class="code-compare">
169
- <div class="col">
170
- <div class="col-head">{left_title}</div>
171
- <pre><code class="language-python">{escape(left)}</code></pre>
172
- </div>
173
- <div class="col">
174
- <div class="col-head">{right_title}</div>
175
- <pre><code class="language-python">{escape(right)}</code></pre>
176
- </div>
177
- </div>
178
- """)
179
- return _build
180
-
181
- def build_terminal():
182
- with gr.Group():
183
- cmd = gr.Textbox(label="Command", value="python -c 'import torch; print(torch.__version__)'")
184
- run = gr.Button("Run")
185
- out = gr.Textbox(label="Output", lines=12, interactive=False, elem_classes=["monospace-output"])
186
- run.click(run_shell, inputs=cmd, outputs=out, scroll_to_output=False)
187
-
188
- # ---------------------------------------
189
- # Attention Mask Visualizer (Transformers)
190
- # ---------------------------------------
191
-
192
- def _import_attention_visualizer():
193
- try:
194
- from transformers.utils.attention_visualizer import AttentionMaskVisualizer
195
- except Exception as e:
196
- raise RuntimeError(
197
- "AttentionMaskVisualizer is unavailable in this Transformers version."
198
- ) from e
199
- return AttentionMaskVisualizer
200
-
201
- @spaces.GPU(duration=120)
202
- def render_attention_mask(model_id: str, prompt: str) -> str:
203
- try:
204
- AttentionMaskVisualizer = _import_attention_visualizer()
205
- vis = AttentionMaskVisualizer(model_id)
206
- out = vis(prompt)
207
- return str(out)
208
- except Exception as e:
209
- return f"<p>Attention visualizer error: {e}</p>"
210
-
211
- def build_attn_vis():
212
- with gr.Group():
213
- with gr.Row():
214
- model = gr.Dropdown(
215
- label="Model",
216
- choices=["openai-community/gpt2", "google/gemma-2-2b"],
217
- value="openai-community/gpt2",
218
- allow_custom_value=True,
219
- )
220
- prompt = gr.Textbox(label="Prompt", value="You are an assistant. Make sure you print me.")
221
- go = gr.Button("Render")
222
- html = gr.HTML()
223
- go.click(render_attention_mask, inputs=[model, prompt], outputs=html, scroll_to_output=False)
224
-
225
- # -------------------------------------------------------
226
- # Transformers caching allocator warmup (time vs MiB plot)
227
- # -------------------------------------------------------
228
-
229
-
230
- def _measure_load_timeline(model_id: str, disable_warmup: bool):
231
- """Measure memory usage during model loading with/without cache warmup."""
232
- original_warmup_func = getattr(transformers_modeling, "caching_allocator_warmup", None)
233
- if disable_warmup and original_warmup_func is not None:
234
- transformers_modeling.caching_allocator_warmup = lambda *args, **kwargs: None
235
-
236
- try:
237
- device = "cuda" if torch.cuda.is_available() else "cpu"
238
- timeline_data = []
239
-
240
- def sample_memory(start_time, stop_event):
241
- while not stop_event.is_set():
242
- if device == "cuda":
243
- torch.cuda.synchronize()
244
- allocated_memory = torch.cuda.max_memory_allocated()
245
- torch.cuda.reset_peak_memory_stats()
246
- else:
247
- allocated_memory = 0
248
- timeline_data.append({
249
- "t": time.perf_counter() - start_time,
250
- "MiB": allocated_memory / (1024**2)
251
- })
252
- time.sleep(0.02)
253
-
254
- if device == "cuda":
255
- torch.cuda.empty_cache()
256
- torch.cuda.reset_peak_memory_stats()
257
-
258
- start_time = time.perf_counter()
259
- stop_event = threading.Event()
260
- memory_thread = threading.Thread(target=sample_memory, args=(start_time, stop_event), daemon=True)
261
- memory_thread.start()
262
-
263
- model_kwargs = {"low_cpu_mem_usage": True}
264
- if device == "cuda":
265
- model_kwargs.update({
266
- "torch_dtype": torch.float16,
267
- "device_map": "cuda:0"
268
- })
269
-
270
- model = AutoModelForCausalLM.from_pretrained(model_id, **model_kwargs)
271
-
272
- stop_event.set()
273
- memory_thread.join()
274
-
275
- if device == "cuda":
276
- torch.cuda.synchronize()
277
- final_memory = torch.cuda.memory_allocated()
278
- timeline_data.append({
279
- "t": time.perf_counter() - start_time,
280
- "MiB": final_memory / (1024**2)
281
- })
282
-
283
- del model
284
- if device == "cuda":
285
- torch.cuda.empty_cache()
286
- torch.cuda.ipc_collect()
287
-
288
- return timeline_data
289
- finally:
290
- if original_warmup_func is not None:
291
- transformers_modeling.caching_allocator_warmup = original_warmup_func
292
-
293
- @spaces.GPU(duration=240)
294
- def profile_warmup_comparison(model_id: str):
295
- """Profile memory usage with and without cache warmup."""
296
- if not torch.cuda.is_available():
297
- time_points = np.linspace(0, 5, 50)
298
- base_memory = np.cumsum(np.random.exponential(50, 50))
299
- warmup_enabled_data = [
300
- {"t": t, "MiB": mem, "mode": "🚀 Warmup ON (Optimized)"}
301
- for t, mem in zip(time_points, base_memory * 0.8)
302
- ]
303
- warmup_disabled_data = [
304
- {"t": t, "MiB": mem, "mode": "📈 Warmup OFF (Standard)"}
305
- for t, mem in zip(time_points, base_memory)
306
- ]
307
- return pd.DataFrame(warmup_enabled_data + warmup_disabled_data)
308
-
309
- try:
310
- warmup_enabled_timeline = _measure_load_timeline(model_id, disable_warmup=False)
311
- warmup_disabled_timeline = _measure_load_timeline(model_id, disable_warmup=True)
312
-
313
- all_data = []
314
- all_data.extend([
315
- {"t": entry["t"], "MiB": entry["MiB"], "mode": "🚀 Warmup ON (Optimized)"}
316
- for entry in warmup_enabled_timeline
317
- ])
318
- all_data.extend([
319
- {"t": entry["t"], "MiB": entry["MiB"], "mode": "📈 Warmup OFF (Standard)"}
320
- for entry in warmup_disabled_timeline
321
- ])
322
-
323
- result_dataframe = pd.DataFrame(all_data)
324
-
325
- if warmup_enabled_timeline and warmup_disabled_timeline:
326
- peak_with_warmup = max(entry["MiB"] for entry in warmup_enabled_timeline)
327
- peak_without_warmup = max(entry["MiB"] for entry in warmup_disabled_timeline)
328
- if peak_without_warmup > 0:
329
- savings_percent = ((peak_without_warmup - peak_with_warmup) / peak_without_warmup * 100)
330
- print(f"Memory savings: {savings_percent:.1f}% (Peak: {peak_with_warmup:.0f} MiB vs {peak_without_warmup:.0f} MiB)")
331
-
332
- return result_dataframe
333
- except Exception as error:
334
- print(f"Error profiling {model_id}: {error}")
335
- return pd.DataFrame(columns=["t", "MiB", "mode"])
336
-
337
- def build_alloc_plot():
338
- with gr.Group():
339
- gr.Markdown("### 🚀 Cache Pre-allocator Performance Demo")
340
- gr.Markdown("Compare model loading with and without transformers' caching allocator warmup. This demonstrates the memory efficiency improvements.")
341
-
342
- with gr.Row():
343
- model = gr.Dropdown(
344
- label="Model to Profile",
345
- choices=[
346
- "openai-community/gpt2",
347
- "google/gemma-2-2b",
348
- "microsoft/DialoGPT-small",
349
- "facebook/opt-125m"
350
- ],
351
- value="openai-community/gpt2",
352
- allow_custom_value=True,
353
- info="Select a model or enter a custom HuggingFace model ID"
354
- )
355
- go = gr.Button("🔥 Profile Memory", variant="primary")
356
-
357
- plot = gr.LinePlot(
358
- x="t", y="MiB", color="mode", overlay_point=True,
359
- title="Memory Allocation Timeline: Warmup ON vs OFF",
360
- tooltip=["t", "MiB", "mode"],
361
- width=900, height=450,
362
- x_title="Time (seconds)",
363
- y_title="Memory (MiB)"
364
- )
365
-
366
- gr.Markdown("**Note**: This demo requires GPU access. The warmup feature reduces peak memory usage during model loading.")
367
- go.click(profile_warmup_comparison, inputs=[model], outputs=plot, scroll_to_output=False)
368
-
369
- # ---------------------------
370
- # Optional FastRTC preview
371
- # ---------------------------
372
-
373
- try:
374
- from fastrtc import WebRTC, ReplyOnPause
375
- def _echo_video(frame):
376
- yield frame
377
- HAS_FASTRTC = True
378
- except Exception:
379
- HAS_FASTRTC = False
380
-
381
- def build_fastrtc():
382
- if not HAS_FASTRTC:
383
- gr.Markdown("Install `fastrtc` to enable this section.")
384
- return
385
-
386
- def echo_video_frame(frame):
387
- yield frame
388
-
389
- with gr.Group():
390
- gr.Markdown("Camera loopback using FastRTC WebRTC. Extend with streaming handlers later.")
391
- webrtc_component = WebRTC(mode="send-receive", modality="video")
392
- webrtc_component.stream(ReplyOnPause(echo_video_frame), inputs=[webrtc_component], outputs=[webrtc_component], time_limit=60)
393
-
394
- # ---------------------------
395
- # Image display functions
396
- # ---------------------------
397
-
398
- def build_image(filename):
399
- def _build():
400
- # Try both content/ and static/ directories
401
- for directory in ['content', 'static']:
402
- filepath = Path(directory) / filename
403
- if filepath.exists():
404
- gr.Image(value=str(filepath), show_label=False, interactive=False, show_download_button=False)
405
- return
406
- gr.Markdown(f"*Image not found: {filename}*")
407
- return _build
408
-
409
- def build_d3_graph():
410
- with gr.Group():
411
- gr.Markdown("### 🔗 Interactive Model Dependency Graph")
412
- gr.Markdown("Explore how transformers models inherit from each other using the modular system. Click and drag nodes to interact!")
413
-
414
- html_file = Path("static/d3_dependency_graph.html")
415
- html_content = html_file.read_text(encoding="utf-8")
416
-
417
- body_start = html_content.find('<body')
418
- body_end = html_content.find('</body>') + 7
419
- if body_start != -1 and body_end != -1:
420
- body_content = html_content[body_start:body_end]
421
- body_content = re.sub(r'</?body[^>]*>', '', body_content)
422
- else:
423
- body_content = html_content
424
-
425
- scripts = re.findall(r'<script[^>]*>.*?</script>', html_content, re.DOTALL)
426
- script_content = '\n'.join(scripts)
427
-
428
- styles = re.findall(r'<style[^>]*>.*?</style>', html_content, re.DOTALL)
429
- style_content = '\n'.join(styles)
430
-
431
- # Fix the D3.js sizing and HF logo issues
432
- script_content = script_content.replace('window.innerWidth', 'document.getElementById("d3-graph-container").offsetWidth')
433
- script_content = script_content.replace('window.innerHeight', 'document.getElementById("d3-graph-container").offsetHeight')
434
- script_content = script_content.replace("'hf-logo.svg'", "'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0iI0ZGQkUwQiIvPgo8cGF0aCBkPSJNMTIgMTcuOEw1LjkgOS40aDMuMVY4LjNoNnYxLjFoMy4xTDEyIDE3Ljh6IiBmaWxsPSJibGFjayIvPgo8L3N2Zz4K'")
435
-
436
- # Fix CSS to work in container
437
- style_content = style_content.replace('100vw', '100%').replace('100vh', '100%')
438
- style_content = style_content.replace('overflow: hidden;', 'overflow: visible;')
439
-
440
- embedded_html = f'''
441
- <div id="d3-graph-container" style="width: 100%; height: 640px; border: 1px solid #e2e8f0; border-radius: 8px; background: white; position: relative;">
442
- {body_content}
443
- </div>
444
- {style_content}
445
- <script>
446
- // Wait for container to be ready
447
- setTimeout(function() {{
448
- {script_content.replace('<script>', '').replace('</script>', '')}
449
- }}, 100);
450
- </script>
451
- '''
452
- gr.HTML(embedded_html)
453
-
454
- # ---------------------------
455
- # Inserts registry
456
- # ---------------------------
457
-
458
- INSERTS = {
459
- "TERMINAL": build_terminal,
460
- "ATTN_VIS": build_attn_vis,
461
- "ALLOC_PLOT": build_alloc_plot,
462
- "D3_GRAPH": build_d3_graph,
463
- # Image inserts
464
- "GRAPH_MODULAR_RELATED_MODELS": build_image("graph_modular_related_models.png"),
465
- "JACCARD_SIMILARITY_PLOT": build_image("Jaccard_similarity_plot.png"),
466
- "BLOATEDNESS_VISUALIZER": build_image("Bloatedness_visualizer.png"),
467
- "MODULAR_CANDIDATES": build_image("modular_candidates.png"),
468
- "POPULAR_MODELS_BARPLOT": build_image("popular_models_barplot.png"),
469
- "MODEL_DEBUGGER": build_image("model_debugger.png"),
470
- "GLM_COMPARE": build_code_compare(
471
- "content/modular_glm.py",
472
- "content/modeling_glm.py",
473
- left_title="modular_glm.py",
474
- right_title="modeling_glm.py (auto-expanded)"
475
- )
476
- }
477
-
478
- # ---------------------------
479
- # Layout / CSS / App
480
- # ---------------------------
481
- HLJS = """
482
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.min.css">
483
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
484
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
485
- <script>
486
- document.addEventListener('DOMContentLoaded', function() {
487
- function highlightCode() {
488
- // Default to python for code blocks without a language
489
- document.querySelectorAll('pre code:not([class*="language-"]):not(.hljs)').forEach((block) => {
490
- block.classList.add('language-python');
491
- });
492
-
493
- // Highlight all non-highlighted blocks
494
- document.querySelectorAll('pre code:not(.hljs)').forEach((block) => {
495
- hljs.highlightElement(block);
496
- });
497
-
498
- // Force highlighting on code-compare blocks specifically
499
- document.querySelectorAll('.code-compare pre code').forEach((block) => {
500
- if (!block.classList.contains('hljs')) {
501
- block.classList.add('language-python');
502
- hljs.highlightElement(block);
503
- }
504
- });
505
-
506
- // Apply custom 'tenet' styling
507
- document.querySelectorAll('.article ol > li').forEach((li) => {
508
- if (li.querySelector(':scope > a[id]')) {
509
- li.classList.add('tenet');
510
- }
511
- });
512
- }
513
-
514
- function overrideTocPosition() {
515
- const toc = document.getElementById('toc-column');
516
- const layoutRow = document.getElementById('layout-row');
517
-
518
- if (toc && window.innerWidth >= 900) {
519
- // Override CSS with JavaScript - more reliable than CSS for Gradio
520
- toc.style.cssText = `
521
- position: fixed !important;
522
- top: 0 !important;
523
- left: 0 !important;
524
- height: 100vh !important;
525
- width: 320px !important;
526
- z-index: 9999 !important;
527
- overflow-y: auto !important;
528
- background: white !important;
529
- border-right: 1px solid #e5e7eb !important;
530
- padding: 1rem !important;
531
- box-sizing: border-box !important;
532
- `;
533
-
534
- // Move content to avoid overlap
535
- if (layoutRow) {
536
- layoutRow.style.paddingLeft = '336px';
537
- }
538
- } else if (toc) {
539
- // Mobile: reset to normal
540
- toc.style.cssText = 'padding: 1rem;';
541
- if (layoutRow) {
542
- layoutRow.style.paddingLeft = '0';
543
- }
544
- }
545
- }
546
-
547
- highlightCode();
548
- overrideTocPosition();
549
-
550
- // Re-apply on window resize
551
- window.addEventListener('resize', overrideTocPosition);
552
-
553
- // Use a MutationObserver to re-run highlighting when Gradio loads dynamic content.
554
- const observer = new MutationObserver((mutations) => {
555
- const needsHighlight = mutations.some(m => m.type === 'childList' && m.addedNodes.length > 0);
556
- if (needsHighlight) {
557
- // A small delay allows the DOM to settle before highlighting
558
- setTimeout(() => {
559
- highlightCode();
560
- overrideTocPosition();
561
- }, 100);
562
- }
563
- });
564
-
565
- observer.observe(document.body, {
566
- childList: true,
567
- subtree: true
568
- });
569
- });
570
- </script>
571
- """
572
-
573
-
574
- CSS = """
575
- /* ================= Theme & base ================= */
576
- :root{
577
- --bg:#fff;
578
- --text:#0b0f19;
579
- --heading:#0b0f19;
580
- --link:#1d4ed8;
581
- --border:#e5e7eb;
582
- --muted:#334155;
583
- --tocw:320px; /* sidebar width on desktop */
584
- --mono: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace;
585
- --sans: Inter, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
586
- }
587
-
588
- html,body,.gradio-container{
589
- background:var(--bg) !important;
590
- color:var(--text) !important;
591
- font-family:var(--sans) !important;
592
- -webkit-font-smoothing:antialiased;
593
- color-scheme:light !important;
594
- }
595
-
596
- /* Gradio sometimes applies transforms/overflow that break fixed elements.
597
- These two lines keep the TOC truly fixed and visible. */
598
- .gradio-container{ overflow: visible !important; }
599
- .gradio-container, .gradio-container > * { transform: none !important; }
600
-
601
- /* Keep embedded HTML from being dimmed */
602
- .gradio-container .gr-html,
603
- .gradio-container .gr-html *{ opacity:1 !important; }
604
-
605
- /* Strong headings even outside .article (e.g., “FastRTC (preview)”) */
606
- .gradio-container h1,
607
- .gradio-container h2,
608
- .gradio-container h3,
609
- .gradio-container h4{
610
- color:var(--heading) !important;
611
- font-weight:700 !important;
612
- }
613
-
614
- /* ================= TOC: always visible ================= */
615
- @media (min-width: 900px){
616
- /* Pin the TOC to the viewport */
617
- #toc-column{
618
- position: fixed !important;
619
- top: 0 !important; left: 0 !important;
620
- height: 100vh !important;
621
- width: var(--tocw) !important;
622
- background: var(--bg) !important;
623
- border-right: 1px solid var(--border) !important;
624
- z-index: 3000 !important;
625
- overflow-y: auto !important;
626
- padding: 1rem !important;
627
- }
628
- /* Reserve space so content/title never sits under the fixed TOC */
629
- body, .gradio-container, #layout{
630
- box-sizing: border-box !important;
631
- padding-left: calc(var(--tocw) + 16px) !important;
632
- }
633
- }
634
-
635
- @media (max-width: 899.98px){
636
- #toc-column{ position: static !important; width:auto !important; height:auto !important; border-right:none !important; padding:0 !important; }
637
- body, .gradio-container, #layout{ padding-left: 0 !important; }
638
- }
639
-
640
- /* TOC links */
641
- #toc a{
642
- display:block;
643
- padding:.5rem 0;
644
- color:var(--muted);
645
- font-size:1rem;
646
- text-decoration:none;
647
- font-weight:500;
648
- }
649
- #toc a:hover{ color:var(--link); }
650
-
651
- /* Layout container */
652
- #layout{ display:block !important; max-width:1400px !important; margin:0 auto !important; }
653
-
654
- /* ================= Article typography ================= */
655
- .article{
656
- max-width:80ch;
657
- margin:0 auto;
658
- color:var(--text);
659
- }
660
- .article p,.article li{ font-size:1.075rem; line-height:1.85rem; color:var(--text) !important; font-weight:500; }
661
- .article h1,.article h2,.article h3,.article h4{
662
- color:var(--heading) !important;
663
- margin:1.5rem 0 .75rem;
664
- }
665
- .article h1{ font-size:2.25rem; line-height:2.6rem; margin-top:2rem; font-weight:700; }
666
- .article h2{ font-size:1.85rem; line-height:2.25rem; font-weight:650; }
667
- .article h3{ font-size:1.5rem; line-height:2rem; font-weight:600; }
668
-
669
- .article a{ color:var(--link) !important; text-decoration:underline; }
670
- .article a:hover{ text-decoration:none; }
671
-
672
- .section{ scroll-margin-top:80px; }
673
-
674
- .article blockquote{
675
- border-left:4px solid var(--link);
676
- padding-left:1rem;
677
- margin:1.25rem 0;
678
- color:#334155 !important;
679
- font-style:italic;
680
- }
681
- .article img{
682
- display:block;
683
- max-width:100%;
684
- height:auto;
685
- margin:1.25rem auto;
686
- border-radius:8px;
687
- box-shadow:0 6px 20px rgba(0,0,0,.08);
688
- }
689
-
690
- /* ================= Code: fenced blocks ================= */
691
- .article pre{
692
- background:#f8fafc !important;
693
- border:1px solid var(--border) !important;
694
- border-radius:8px !important;
695
- padding:1.1rem !important;
696
- margin:1.25rem 0 !important;
697
- overflow-x:auto !important;
698
- font-family:var(--mono) !important;
699
- font-size:.93rem !important;
700
- line-height:1.6 !important;
701
- }
702
-
703
- /* Make *all* text inside code blocks dark by default (overrides HLJS base) */
704
- .article pre code,
705
- .article pre code *,
706
- .code-compare pre code,
707
- .code-compare pre code *{
708
- color:#0b0f19 !important;
709
- }
710
-
711
- /* Keep HLJS background transparent so our container bg shows */
712
- .hljs{ background:transparent !important; }
713
-
714
- /* Optional: slightly higher-contrast token accents - apply to both article and code-compare */
715
- .article .hljs-keyword, .article .hljs-built_in, .code-compare .hljs-keyword, .code-compare .hljs-built_in{ color:#3b33d6 !important; font-weight:600 !important; }
716
- .article .hljs-string, .code-compare .hljs-string{ color:#065f46 !important; }
717
- .article .hljs-comment, .code-compare .hljs-comment{ color:#475569 !important; font-style:italic !important; }
718
- .article .hljs-number, .article .hljs-literal, .code-compare .hljs-number, .code-compare .hljs-literal{ color:#9f1239 !important; }
719
- .article .hljs-title, .article .hljs-function .hljs-title, .code-compare .hljs-title, .code-compare .hljs-function .hljs-title{ color:#1d4ed8 !important; font-weight:600 !important; }
720
- .article .hljs-attr, .article .hljs-attribute, .code-compare .hljs-attr, .code-compare .hljs-attribute{ color:#0f766e !important; }
721
- .article .hljs-type, .code-compare .hljs-type{ color:#3b33d6 !important; }
722
-
723
- /* ================= Code: inline backticks ================= */
724
- .article code{
725
- background:#0f172a;
726
- color:#e2e8f0;
727
- padding:.18em .35em;
728
- border-radius:4px;
729
- font-size:.95em;
730
- font-family:var(--mono);
731
- }
732
- /* keep link color when code is inside a link */
733
- .article a code{ color: currentColor; }
734
-
735
- /* ================= Tenets ================= */
736
- .article ol > li.tenet{
737
- border-left:4px solid var(--link);
738
- background:#f8fafc;
739
- padding:.75rem 1rem;
740
- margin:.5rem 0;
741
- border-radius:8px;
742
- }
743
- .article ol > li.tenet::marker{ color:var(--link); font-weight:700; }
744
-
745
- /* ================= Two-column code compare ================= */
746
- .code-compare{
747
- display:grid;
748
- grid-template-columns:1fr 1fr;
749
- gap:1rem;
750
- align-items:start;
751
- margin:1rem 0;
752
- }
753
- .code-compare .col{
754
- border:1px solid var(--border);
755
- border-radius:8px;
756
- background:#fafafa;
757
- overflow:hidden;
758
- }
759
- .code-compare .col-head{
760
- font-size:.9rem;
761
- font-weight:600;
762
- color:#475569;
763
- padding:.75rem 1rem;
764
- background:#f8fafc;
765
- border-bottom:1px solid var(--border);
766
- }
767
- .code-compare .col pre{
768
- max-height:640px;
769
- overflow:auto;
770
- margin:0 !important;
771
- border:none !important;
772
- border-radius:0 !important;
773
- }
774
- @media (max-width:1100px){ .code-compare{ grid-template-columns:1fr; } }
775
-
776
- /* ================= Gradio widgets (light touch) ================= */
777
- .gr-form,.gr-panel,.gr-block{ background:var(--bg) !important; border:1px solid var(--border) !important; border-radius:8px !important; }
778
- .gr-button{ background:var(--link) !important; color:#fff !important; border:0 !important; border-radius:6px !important; font-weight:600 !important; }
779
- .gr-button:hover{ filter:brightness(0.95); }
780
- .gr-textbox textarea{ background:#f8fafc !important; border:1px solid var(--border) !important; border-radius:8px !important; }
781
- .gr-textbox textarea[readonly]{ background:#111827 !important; color:#f9fafb !important; border:1px solid #374151 !important; }
782
- .gr-dropdown,.gr-dropdown .gr-box{ background:#fff !important; border:1px solid var(--border) !important; border-radius:8px !important; }
783
- """
784
- TOC_FIX = """
785
- <style>
786
- /* --- Keep fixed elements from breaking inside Gradio containers --- */
787
- .gradio-container{ overflow:visible !important; }
788
- .gradio-container, .gradio-container > *{ transform:none !important; }
789
-
790
- /* --- Host created by the script; this is the real fixed TOC --- */
791
- #toc-fixed{
792
- position:fixed; top:0; left:0;
793
- height:100vh; width:320px; /* matches your --tocw */
794
- z-index:9999; overflow-y:auto;
795
- background:#fff; border-right:1px solid #e5e7eb;
796
- padding:1rem; box-sizing:border-box;
797
- }
798
-
799
- /* When fixed mode is active, keep the original column's space but hide its content */
800
- .toc-fixed-active #toc-column{ visibility:hidden; }
801
-
802
- /* Mobile: disable fixed TOC (normal flow) */
803
- @media (max-width: 899.98px){
804
- #toc-fixed{ display:none; }
805
- .toc-fixed-active #toc-column{ visibility:visible; }
806
- }
807
- </style>
808
-
809
- <script>
810
- (function(){
811
- const MIN_W = 900; // desktop threshold
812
-
813
- function mountFixedTOC(){
814
- const col = document.getElementById('toc-column');
815
- const nav = col && col.querySelector('nav#toc');
816
- if(!col || !nav) return;
817
-
818
- const isDesktop = window.innerWidth >= MIN_W;
819
- let fixed = document.getElementById('toc-fixed');
820
-
821
- if(isDesktop){
822
- // create fixed host if missing
823
- if(!fixed){
824
- fixed = document.createElement('aside');
825
- fixed.id = 'toc-fixed';
826
- document.body.appendChild(fixed);
827
- }
828
- // move the nav into the fixed host
829
- if(nav.parentElement !== fixed){
830
- fixed.innerHTML = '';
831
- fixed.appendChild(nav);
832
- }
833
- // align the fixed TOC with the placeholder column
834
- const rect = col.getBoundingClientRect();
835
- fixed.style.left = (rect.left + window.scrollX) + 'px';
836
- fixed.style.width = getComputedStyle(col).width;
837
-
838
- document.documentElement.classList.add('toc-fixed-active');
839
- }else{
840
- // mobile: put nav back into the original column
841
- if(fixed && nav.parentElement === fixed){
842
- col.appendChild(nav);
843
- }
844
- document.documentElement.classList.remove('toc-fixed-active');
845
- }
846
- }
847
-
848
- // Run on load, after hydration, on resize, and when layout shifts
849
- document.addEventListener('DOMContentLoaded', mountFixedTOC);
850
- window.addEventListener('resize', mountFixedTOC);
851
- setTimeout(mountFixedTOC, 300);
852
-
853
- const target = document.querySelector('.gradio-container') || document.body;
854
- new ResizeObserver(mountFixedTOC).observe(target);
855
- })();
856
- </script>
857
- """
858
-
859
-
860
- with gr.Blocks(css=CSS, fill_height=True, title="Interactive Blog — Transformers Feature Showcase") as demo:
861
- gr.HTML(HLJS)
862
- gr.HTML(TOC_FIX)
863
- with gr.Row(elem_id="layout-row"):
864
- # This column holds the Table of Contents. It is fixed on desktop.
865
- with gr.Column(scale=1, min_width=300, elem_id="toc-column"):
866
- gr.HTML(
867
- """
868
- <nav id="toc">
869
- <h3>Contents</h3>
870
- <a href="#introduction">Introduction</a>
871
- <a href="#what-you-will-learn">What you will learn</a>
872
- <div>
873
- <a href="#source-of-truth">0. Source of truth</a>
874
- <a href="#one-model-one-file">1. One model, one file</a>
875
- <a href="#code-is-product">2. Code is product</a>
876
- <a href="#standardize-dont-abstract">3. Standardize, don't abstract</a>
877
- <a href="#do-repeat-yourself">4. DRY* (DO Repeat Yourself)</a>
878
- <a href="#minimal-user-api">5. Minimal user API</a>
879
- <a href="#backwards-compatibility">6. Backwards compatibility</a>
880
- <a href="#consistent-public-surface">7. Consistent public surface</a>
881
- </div>
882
- <a href="#modular">Going modular</a>
883
- <a href="#attention-classes">External Attention classes</a>
884
- <a href="#community-kernels">Community Kernels</a>
885
- <a href="#the-good-modularity">The good modularity</a>
886
- <a href="#too-many-models">Too many models, yet not enough, are alike</a>
887
- <a href="#vlm-improvements">VLM improvements, avoiding abstraction</a>
888
- <a href="#modularity-candidates">Modularity candidates</a>
889
- <a href="#encoders-ftw">Encoders win!</a>
890
- <a href="#on-image-processing">On image processing and processors</a>
891
- <a href="#reduce-barrier-to-entry">Reduce barrier to entry/contribution</a>
892
- <a href="#surgical-toolbox">A surgical toolbox for model development</a>
893
- <div>
894
- <a href="#attention-visualisation">Attention visualisation</a>
895
- <a href="#transformers-serve">Transformers-serve</a>
896
- </div>
897
- <a href="#community-reusability">Community reusability</a>
898
- <a href="#inner-cooking">Inner cooking: Cache allocator</a>
899
- <a href="#rtc">FastRTC (preview)</a>
900
- </nav>
901
- """
902
- )
903
-
904
- # This column holds the main article content.
905
- with gr.Column(scale=4):
906
- # The title must be inside the main content column to respect the layout.
907
- gr.HTML("<h1>Transformers Feature Showcase</h1><p>An interactive, scrollable demonstration of key features and design tenets in the <code>transformers</code> library.</p>")
908
-
909
- # The article content is rendered here.
910
- render_article("content/article.md", INSERTS)
911
-
912
- gr.HTML("<hr/>")
913
- gr.HTML('<h2 id="rtc" class="section">FastRTC (preview)</h2>')
914
- build_fastrtc()
915
-
916
- if __name__ == "__main__":
917
- demo.launch()