diff --git a/README.md b/README.md index 9ebc99574df11487fbe3b6ccbcdf596db69a9b75..dccef597ce0937b246b4139a2b659b651ff31045 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ --- title: Transformers Timeline -emoji: ๐Ÿ“Š -colorFrom: green -colorTo: gray +emoji: ๐Ÿค— +colorFrom: blue +colorTo: purple sdk: gradio sdk_version: 5.46.0 app_file: app.py pinned: false license: apache-2.0 -short_description: Interactive timeline to explore the๐Ÿค—Transformers models +short_description: Interactive timeline to explore the ๐Ÿค—Transformers models --- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..a3b7b160ffc84569b62c6ed32ff7a2054bc56980 --- /dev/null +++ b/app.py @@ -0,0 +1,2921 @@ +#!/usr/bin/env python3 +""" +Beautiful custom timeline visualization for Transformers models using Flask. +""" + +import glob +import os +import re +import sys +import threading +import time +import webbrowser +from datetime import datetime +from typing import Dict, List, Optional + +from flask import Flask, jsonify, render_template, request + + +class TransformersTimelineParser: + """Parser for extracting model release dates from Transformers documentation.""" + + def __init__(self, docs_dir: str): + self.docs_dir = docs_dir + self.models_cache = None + self.tasks_cache = {} + + # Add transformers source directory to Python path to import auto mappings + transformers_src = os.path.join(os.path.dirname(docs_dir), "..", "..", "src") + if transformers_src not in sys.path: + sys.path.insert(0, transformers_src) + + # Modality definitions with modern color scheme + self.modalities = { + "text": { + "name": "Text Models", + "color": "#8B5CF6", # Soft purple + "models": [ + "albert", + "apertus", + "arcee", + "bamba", + "bart", + "barthez", + "bartpho", + "bert", + "bert-generation", + "bert-japanese", + "bertweet", + "big_bird", + "bigbird_pegasus", + "biogpt", + "bitnet", + "blenderbot", + "blenderbot-small", + "bloom", + "bort", + "byt5", + "camembert", + "canine", + "codegen", + "code_llama", + "cohere", + "cohere2", + "convbert", + "cpm", + "cpmant", + "ctrl", + "dbrx", + "deberta", + "deberta-v2", + "deepseek_v3", + "dialogpt", + "diffllama", + "distilbert", + "doge", + "dots1", + "dpr", + "electra", + "encoder-decoder", + "ernie", + "ernie4_5", + "ernie4_5_moe", + "ernie_m", + "esm", + "exaone4", + "falcon", + "falcon3", + "falcon_h1", + "falcon_mamba", + "flan-t5", + "flan-ul2", + "flaubert", + "fnet", + "fsmt", + "funnel", + "fuyu", + "gemma", + "gemma2", + "glm", + "glm4", + "glm4_moe", + "openai-gpt", + "gpt_neo", + "gpt_neox", + "gpt_neox_japanese", + "gptj", + "gpt2", + "gpt_bigcode", + "gpt_oss", + "gptsan-japanese", + "gpt-sw3", + "granite", + "granitemoe", + "granitemoehybrid", + "granitemoeshared", + "helium", + "herbert", + "hgnet_v2", + "hunyuan_v1_dense", + "hunyuan_v1_moe", + "ibert", + "jamba", + "jetmoe", + "jukebox", + "led", + "lfm2", + "llama", + "llama2", + "llama3", + "longformer", + "longt5", + "luke", + "m2m_100", + "madlad-400", + "mamba", + "mamba2", + "marian", + "markuplm", + "mbart", + "mega", + "megatron-bert", + "megatron_gpt2", + "minimax", + "ministral", + "mistral", + "mixtral", + "mluke", + "mobilebert", + "modernbert", + "modernbert-decoder", + "mpnet", + "mpt", + "mra", + "mt5", + "mvp", + "myt5", + "nemotron", + "nezha", + "nllb", + "nllb-moe", + "nystromformer", + "olmo", + "olmo2", + "olmo3", + "olmoe", + "open-llama", + "opt", + "pegasus", + "pegasus_x", + "persimmon", + "phi", + "phi3", + "phimoe", + "phobert", + "plbart", + "prophetnet", + "qdqbert", + "qwen2", + "qwen2_moe", + "qwen3", + "qwen3_moe", + "qwen3_next", + "rag", + "realm", + "recurrent_gemma", + "reformer", + "rembert", + "retribert", + "roberta", + "roberta-prelayernorm", + "roc_bert", + "roformer", + "rwkv", + "seed_oss", + "splinter", + "squeezebert", + "stablelm", + "starcoder2", + "switch_transformers", + "t5", + "t5gemma", + "t5v1.1", + "tapex", + "transfo-xl", + "ul2", + "umt5", + "vaultgemma", + "xmod", + "xglm", + "xlm", + "xlm-prophetnet", + "xlm-roberta", + "xlm-roberta-xl", + "xlm-v", + "xlnet", + "xlstm", + "yoso", + "zamba", + "zamba2", + ], + }, + "vision": { + "name": "Vision Models", + "color": "#06B6D4", # Soft cyan + "models": [ + "aimv2", + "beit", + "bit", + "conditional_detr", + "convnext", + "convnextv2", + "cvt", + "d_fine", + "dab-detr", + "deepseek_v2", + "deepseek_vl", + "deepseek_vl_hybrid", + "deformable_detr", + "deit", + "depth_anything", + "depth_anything_v2", + "depth_pro", + "deta", + "detr", + "dinat", + "dinov2", + "dinov2_with_registers", + "dinov3", + "dit", + "dpt", + "efficientformer", + "efficientloftr", + "efficientnet", + "eomt", + "focalnet", + "glpn", + "hgnet_v2", + "hiera", + "ijepa", + "imagegpt", + "levit", + "lightglue", + "mask2former", + "maskformer", + "mlcd", + "mobilenet_v1", + "mobilenet_v2", + "mobilevit", + "mobilevitv2", + "nat", + "poolformer", + "prompt_depth_anything", + "pvt", + "pvt_v2", + "regnet", + "resnet", + "rt_detr", + "rt_detr_v2", + "segformer", + "seggpt", + "superglue", + "superpoint", + "swiftformer", + "swin", + "swinv2", + "swin2sr", + "table-transformer", + "textnet", + "timm_wrapper", + "upernet", + "van", + "vit", + "vit_hybrid", + "vitdet", + "vit_mae", + "vitmatte", + "vit_msn", + "vitpose", + "yolos", + "zoedepth", + ], + }, + "audio": { + "name": "Audio Models", + "color": "#F59E0B", # Soft amber + "models": [ + "audio-spectrogram-transformer", + "bark", + "clap", + "csm", + "dac", + "dia", + "encodec", + "fastspeech2_conformer", + "granite_speech", + "hubert", + "kyutai_speech_to_text", + "mctct", + "mimi", + "mms", + "moonshine", + "moshi", + "musicgen", + "musicgen_melody", + "pop2piano", + "seamless_m4t", + "seamless_m4t_v2", + "sew", + "sew-d", + "speech_to_text", + "speech_to_text_2", + "speecht5", + "unispeech", + "unispeech-sat", + "univnet", + "vits", + "wav2vec2", + "wav2vec2-bert", + "wav2vec2-conformer", + "wav2vec2_phoneme", + "wavlm", + "whisper", + "xcodec", + "xls_r", + "xlsr_wav2vec2", + ], + }, + "video": { + "name": "Video Models", + "color": "#EC4899", # Soft pink + "models": ["timesformer", "vjepa2", "videomae", "vivit"], + }, + "multimodal": { + "name": "Multimodal Models", + "color": "#10B981", # Soft emerald + "models": [ + "align", + "altclip", + "aria", + "aya_vision", + "blip", + "blip-2", + "bridgetower", + "bros", + "chameleon", + "chinese_clip", + "clip", + "clipseg", + "clvp", + "cohere2_vision", + "colpali", + "colqwen2", + "data2vec", + "deplot", + "donut", + "emu3", + "evolla", + "flava", + "florence2", + "gemma3", + "gemma3n", + "git", + "glm4v", + "glm4v_moe", + "got_ocr2", + "granitevision", + "grounding-dino", + "groupvit", + "idefics", + "idefics2", + "idefics3", + "instructblip", + "instructblipvideo", + "internvl", + "janus", + "kosmos-2", + "kosmos2_5", + "layoutlm", + "layoutlmv2", + "layoutlmv3", + "layoutxlm", + "lilt", + "llama4", + "llava", + "llava_next", + "llava_next_video", + "llava_onevision", + "lxmert", + "matcha", + "metaclip_2", + "mgp-str", + "mistral3", + "mllama", + "mm-grounding-dino", + "nougat", + "omdet-turbo", + "oneformer", + "ovis2", + "owlvit", + "owlv2", + "paligemma", + "perceiver", + "perception_lm", + "phi4_multimodal", + "pix2struct", + "pixtral", + "qwen2_5_omni", + "qwen2_5_vl", + "qwen2_audio", + "qwen2_vl", + "qwen3_vl", + "qwen3_vl_moe", + "sam2", + "sam2_video", + "sam", + "sam_hq", + "shieldgemma2", + "siglip", + "siglip2", + "smollm3", + "smolvlm", + "speech-encoder-decoder", + "tapas", + "trocr", + "tvlt", + "tvp", + "udop", + "video_llava", + "vilt", + "vipllava", + "vision-encoder-decoder", + "vision-text-dual-encoder", + "visual_bert", + "voxtral", + "xclip", + ], + }, + "reinforcement": { + "name": "Reinforcement Learning", + "color": "#EF4444", # Soft red + "models": ["decision_transformer", "trajectory_transformer"], + }, + "timeseries": { + "name": "Time Series Models", + "color": "#F97316", # Soft orange + "models": ["autoformer", "informer", "patchtsmixer", "patchtst", "time_series_transformer", "timesfm"], + }, + "graph": { + "name": "Graph Models", + "color": "#6B7280", # Soft gray + "models": ["graphormer"], + }, + } + + def get_model_modality(self, model_name: str) -> Dict[str, str]: + """Determine the modality category for a given model.""" + for modality_key, modality_info in self.modalities.items(): + if model_name in modality_info["models"]: + return {"key": modality_key, "name": modality_info["name"], "color": modality_info["color"]} + # Default to text if not found (most common) + return {"key": "text", "name": "Text Models", "color": "#8B5CF6"} + + def parse_release_date_from_file(self, file_path: str) -> Optional[Dict[str, str]]: + """Parse the release date line from a model documentation file.""" + try: + with open(file_path, "r", encoding="utf-8") as f: + content = f.read() + + # Focus on the end of the sentence - the Transformers addition date is what matters most + pattern = ( + r"\*This model was released on (.+?) and added to Hugging Face Transformers on (\d{4}-\d{2}-\d{2})\.\*" + ) + match = re.search(pattern, content) + + if match: + model_name = os.path.basename(file_path).replace(".md", "") + release_date = match.group(1).strip() + transformers_date = match.group(2) + + # Validate the Transformers date (this is the critical one for our timeline) + try: + datetime.strptime(transformers_date, "%Y-%m-%d") + except ValueError: + return None + + # Handle release_date - could be "None" or an actual date + if release_date.lower() == "none": + release_date = None + else: + # Try to validate as a date, but don't fail if it's not + try: + datetime.strptime(release_date, "%Y-%m-%d") + except ValueError: + # Keep the original value even if it's not a valid date + pass + + # Get modality information + modality = self.get_model_modality(model_name) + + # Extract model description + description = self.extract_model_description(content) + + # Get supported tasks/pipelines + tasks = self.get_model_tasks(model_name) + + return { + "model_name": model_name, + "file_path": file_path, + "release_date": release_date, + "transformers_date": transformers_date, + "modality": modality["key"], + "modality_name": modality["name"], + "modality_color": modality["color"], + "description": description, + "tasks": tasks, + } + + except Exception as e: + print(f"Error processing {file_path}: {e}") + return None + + def extract_model_description(self, content: str) -> str: + """Extract the first 1000 characters of model description, excluding HTML/XML tags.""" + try: + # Remove HTML/XML tags + content_no_tags = re.sub(r"<[^>]+>", "", content) + + # Find the start of the actual description (after the initial metadata) + # Look for the first substantial paragraph after the initial lines + lines = content_no_tags.split("\n") + description_start = 0 + + # Skip initial metadata, imports, and short lines + for i, line in enumerate(lines): + stripped = line.strip() + if ( + len(stripped) > 50 + and not stripped.startswith("#") + and not stripped.startswith("*This model was released") + and not stripped.startswith(" + +
+ + +
+ +
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
โˆ’
+
100%
+
+
+
+
+
+
Loading timeline...
+
+
+
+
+ +
+
+ - +
Total Models
+
+
+ - +
Displayed Models
+
+
+ - +
Date Range
+
+
+
+ + +
+
+ + +
+
+
+ + + +""" + + with open(os.path.join(template_dir, "timeline.html"), "w", encoding="utf-8") as f: + f.write(html_content) + + +def open_browser(): + """Open the browser after a short delay.""" + time.sleep(1.5) + webbrowser.open("http://localhost:5000") + + +def main(): + """Main function to run the timeline app.""" + print("๐Ÿค— Transformers Models Timeline") + print("=" * 50) + + # Create templates + create_timeline_template() + + # Check if docs directory exists + if not os.path.exists(docs_dir): + print(f"โŒ Error: Documentation directory not found at {docs_dir}") + print("Please update the 'docs_dir' variable in the script.") + return + + # Parse models to check if any are found + models = parser.parse_all_model_dates() + if not models: + print(f"โš ๏ธ Warning: No models found with release dates in {docs_dir}") + else: + print(f"โœ… Found {len(models)} models with release dates") + + print("\n๐Ÿš€ Starting timeline server...") + print("๐Ÿ“ฑ Opening in browser...") + print("๐ŸŒ Access at: http://localhost:5000") + print("๐Ÿ›‘ Press Ctrl+C to stop") + + # Start browser opening in background + threading.Thread(target=open_browser, daemon=True).start() + + # Run Flask app + try: + app.run(host="0.0.0.0", port=5000, debug=False) + except KeyboardInterrupt: + print("\n๐Ÿ‘‹ Timeline server stopped") + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c47387fcd521a950a5fbad46a5c2b135401d6a9c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +git+https://github.com/huggingface/transformers.git@main +spaces +flask