Skip to main content
Glama
DOWNLOAD_PROGRESS_SPEC.md8.32 kB
# Download Progress Indicator Specification ## Overview Standardize download progress indicators across VoiceMode CLI for consistent UX when downloading models, services, or other resources. ## Design Principles 1. **Informative**: Show progress, speed, time remaining 2. **Consistent**: Same style across all downloads 3. **Responsive**: Update smoothly without flickering 4. **Accessible**: Work in different terminal environments 5. **Graceful**: Handle unknown file sizes elegantly ## Progress Bar Options ### Option 1: Click Progress Bar (Recommended) Using Click's built-in ProgressBar - integrates well with our CLI framework. ``` Downloading ggml-large-v3.bin (3.1 GB) [████████████████████░░░░░░░░░░░░░░░░] 45% (1.4GB/3.1GB) • 12.5 MB/s • ETA: 02:16 ``` **Pros:** - Built into Click (no new dependencies) - Automatic terminal width detection - Handles ANSI colors well - Can customize format easily **Code example:** ```python import click with click.progressbar( length=total_size, label='Downloading ggml-large-v3.bin', show_eta=True, show_percent=True, show_pos=True, width=40, fill_char='█', empty_char='░' ) as bar: # Update during download bar.update(chunk_size) ``` ### Option 2: Rich Progress Bar Using the Rich library for beautiful terminal output. ``` Downloading Core ML model ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00 ggml-large-v3-encoder.mlmodelc.zip 1.2 GB / 1.2 GB • 15.3 MB/s • ✓ Complete ``` **Pros:** - Beautiful, modern appearance - Multiple progress bars simultaneously - Rich formatting options - Spinners for indeterminate progress **Cons:** - Adds Rich as dependency (~500KB) ### Option 3: Custom Minimal Progress Simple, dependency-free implementation. ``` Downloading: ggml-base.bin [=========== ] 55% | 78.1 MB / 142 MB | 8.2 MB/s | ~8s remaining ``` **Code example:** ```python def print_progress(downloaded, total, speed): percent = (downloaded / total) * 100 bar_length = 20 filled = int(bar_length * downloaded / total) bar = '=' * filled + ' ' * (bar_length - filled) print(f'\r[{bar}] {percent:.0f}% | {downloaded/1024/1024:.1f} MB / {total/1024/1024:.1f} MB | {speed/1024/1024:.1f} MB/s', end='', flush=True) ``` ### Option 4: Spinner for Unknown Size When Content-Length header is missing: ``` Downloading model ⠼ 45.2 MB downloaded (5.3 MB/s) ``` Spinner frames: `⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏` ### Option 5: Block Progress Visual blocks that fill up (good for accessibility): ``` Downloading ggml-small.bin (466 MB) Progress: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░ 45% Speed: 10.2 MB/s | Downloaded: 210 MB | Remaining: ~25s ``` ### Option 6: Verbose Mode (Debug/CI) For CI environments or debug mode: ``` [2024-10-01 13:45:23] Starting download: ggml-large-v3.bin [2024-10-01 13:45:24] Progress: 5% (155 MB / 3100 MB) - 12.3 MB/s [2024-10-01 13:45:25] Progress: 10% (310 MB / 3100 MB) - 13.1 MB/s [2024-10-01 13:45:26] Progress: 15% (465 MB / 3100 MB) - 12.8 MB/s ``` ## Implementation Strategy ### 1. Create Unified Download Function ```python # voice_mode/utils/download.py async def download_with_progress( url: str, destination: Path, description: str = None, style: str = "auto", # auto, bar, spinner, blocks, verbose quiet: bool = False ) -> bool: """ Download file with progress indicator. Args: url: URL to download from destination: Where to save the file description: Label for the progress bar style: Progress indicator style quiet: Suppress all output Returns: True if successful, False otherwise """ ``` ### 2. Environment Detection ```python def detect_progress_style(): """Auto-detect best progress style based on environment.""" if os.environ.get('CI'): return 'verbose' # CI environment if not sys.stdout.isatty(): return 'verbose' # Not a terminal if os.environ.get('VOICEMODE_PROGRESS_STYLE'): return os.environ['VOICEMODE_PROGRESS_STYLE'] return 'bar' # Default to progress bar ``` ### 3. Integration Points **Whisper Models:** ```python # voice_mode/utils/services/whisper_helpers.py async def download_whisper_model(...): await download_with_progress( url=model_url, destination=model_path, description=f"Downloading {model_name}" ) ``` **Core ML Models:** ```python # voice_mode/utils/services/whisper_helpers.py async def download_coreml_model(...): await download_with_progress( url=coreml_url, destination=coreml_zip, description=f"Downloading Core ML model for {model}" ) ``` **Service Installations:** ```python # When downloading Whisper.cpp releases, Kokoro, etc. ``` ## User Configuration ### Environment Variables ```bash # Progress bar style export VOICEMODE_PROGRESS_STYLE=bar # bar, spinner, blocks, verbose, quiet # Colors export VOICEMODE_PROGRESS_COLOR=true # Enable/disable colors # Update frequency export VOICEMODE_PROGRESS_RATE=10 # Updates per second ``` ### CLI Flags ```bash # Global flags voicemode --progress=verbose whisper model install large-v3 voicemode --quiet whisper model install base # Command-specific voicemode whisper model install large-v3 --no-progress voicemode whisper model install large-v3 --progress-style=blocks ``` ## Error Handling ### Network Interruption ``` Downloading ggml-large-v3.bin (3.1 GB) [████████████░░░░░░░░░░░░░░░░░░░░░░] 35% (1.1GB/3.1GB) ❌ Download interrupted: Connection reset by peer 💡 Resume with: voicemode whisper model install large-v3 --resume ``` ### Disk Space ``` Downloading ggml-large-v3.bin (3.1 GB) [██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 18% (558MB/3.1GB) ❌ Insufficient disk space (need 3.1 GB, have 2.1 GB available) ``` ## Testing Considerations 1. **Terminal Widths**: Test with narrow (80 cols) and wide terminals 2. **Color Support**: Test with NO_COLOR environment variable 3. **CI Environment**: Test with CI=true 4. **Network Speeds**: Test progress updates with slow/fast connections 5. **File Sizes**: Test with small (KB), medium (MB), and large (GB) files ## Recommendation **Use Click's ProgressBar (Option 1)** because: - No new dependencies (already using Click) - Good terminal compatibility - Easy to customize - Consistent with CLI framework - Sufficient features for our needs Fallback to spinner (Option 4) when file size is unknown. ## Example Implementation ```python import click import urllib.request from pathlib import Path def download_with_click_progress(url: str, dest: Path, label: str = None): """Download with Click progress bar.""" response = urllib.request.urlopen(url) total_size = int(response.headers.get('Content-Length', 0)) if not label: label = f"Downloading {dest.name}" if total_size == 0: # Unknown size - use spinner with click.progressbar( label=label, length=None, show_percent=False, show_pos=True ) as bar: downloaded = 0 while chunk := response.read(8192): dest.write(chunk) downloaded += len(chunk) bar.update(len(chunk)) else: # Known size - use progress bar with click.progressbar( length=total_size, label=label, show_eta=True, show_percent=True, width=40, fill_char='█', empty_char='░' ) as bar: with open(dest, 'wb') as f: while chunk := response.read(8192): f.write(chunk) bar.update(len(chunk)) ``` ## Next Steps 1. Implement download_with_progress utility function 2. Update whisper_helpers.py to use new function 3. Update CLI to show progress 4. Add --quiet and --verbose flags 5. Test across different environments 6. Document in user guide

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mbailey/voicemode'

If you have feedback or need assistance with the MCP directory API, please join our Discord server