# Phase 3: Rails UI Enhancement & Documentation **Duration**: 2 days **Risk Level**: Low (UI/documentation only, no model changes) **Dependencies**: Phase 1 & 2 complete (4 new quantized models added) ## Objectives 1. Enhance Settings UI to clearly explain quantization options 2. Add user education about INT4 vs INT8 trade-offs 3. Update documentation (README, CLAUDE.md) 4. Create E2E tests for new models 5. Verify full integration ## Current State After Phase 1 & 2, we have: - **10 total models** (up from 6): - Florence-2-base (FP16) - Florence-2-base-int4 (NEW) - Florence-2-large (FP16) - Florence-2-large-int4 (NEW) - SmolVLM-256M-Instruct (BF16) - SmolVLM-256M-Instruct-int8 (NEW) - SmolVLM-500M-Instruct (BF16) - SmolVLM-500M-Instruct-int8 (NEW) - Moondream2 (FP16) - Moondream2-int8 (EXISTING) Current UI: Flat list of 10 cards (cluttered) ## UI Enhancement Options ### Option A: Add Badges & Memory Info (Minimal Change) Keep current card layout but enhance each card with: - **Memory badge**: `💾 ~256MB` or `💾 ~1.5GB` - **Quantization badge**: `[INT4 Quantized]` or `[INT8 Optimized]` - **Recommendation badge**: `[💚 CPU-Friendly]` or `[⚡ GPU Recommended]` **Pros**: Simple, low risk, quick to implement **Cons**: Still 10 separate cards, not grouped ### Option B: Grouped Accordion UI (Optimal, More Work) Group models by family with expandable quantization options: - Florence-2-base ▼ - Full Precision (FP16) - ~460MB - INT4 Optimized - ~115MB [💚 Recommended for <512MB] - SmolVLM-256M ▼ - Full Precision (BF16) - ~500MB - INT8 Optimized - ~256MB [💚 Recommended for CPU] **Pros**: Clean, scalable, educational **Cons**: More complex, requires Stimulus controller changes ### Recommendation: **Option A for Phase 3** Implement Option A (badges) now for quick wins. Option B (accordion) can be a future enhancement when we have more model variants. ## Implementation Steps ### Step 3.1: Update Seeds with Enhanced Descriptions **File**: `meme_search/meme_search_app/db/seeds.rb` **Enhance descriptions** to include hardware recommendations: ```ruby descriptions = [ # Florence-2-base 'Microsoft Florence-2-base (250M params, ~460MB). Balanced performance for general meme captioning. Best for: GPU with 1GB+ VRAM or modern CPU.', # Florence-2-base-int4 'INT4 quantized Florence-2-base (~115MB, 75% reduction). Ultra-compact using NormalFloat4 quantization. Slight quality trade-off (5-10%). Best for: CPU-only or <512MB memory. [Experimental - validate quality]', # Florence-2-large 'Microsoft Florence-2-large (700M params, ~1.54GB). Best quality in Florence series. Best for: GPU with 2GB+ VRAM.', # Florence-2-large-int4 'INT4 quantized Florence-2-large (~385MB, 75% reduction). Best quality under 500MB. Slight quality trade-off (5-10%). Best for: Limited GPU memory or CPU with 1GB+ RAM. [Experimental - validate quality]', # SmolVLM-256M 'Hugging Face SmolVLM-256M (256M params, ~500MB). Compact modern VLM. Best for: Balanced CPU/GPU deployment.', # SmolVLM-256M-int8 'INT8 quantized SmolVLM-256M (~256MB, 49% reduction). Proven 1.7x faster TTFT, minimal quality loss. Best for: CPU-only machines, laptops, edge devices.', # SmolVLM-500M 'Hugging Face SmolVLM-500M (500M params, ~1.8GB). Enhanced quality over 256M. Best for: GPU or high-memory CPU.', # SmolVLM-500M-int8 'INT8 quantized SmolVLM-500M (~500MB, 72% reduction). Best CPU option for quality. Proven 1.4x throughput boost, minimal quality loss. Best for: CPU deployment with quality requirements.', # Moondream2 'Moondream2 (1.9B params, ~5GB). High-quality meme understanding, excellent OCR. Best for: GPU with 6GB+ VRAM.', # Moondream2-int8 'INT8 quantized Moondream2 (~1.5-2GB, 60% reduction). Best quality for memory-constrained setups. Minimal quality loss (0-5%). Best for: CPU-only or GPU with 2-4GB VRAM.' ] ``` ### Step 3.2: Add UI Helper for Memory Badges **File**: `meme_search/meme_search_app/app/helpers/settings/image_to_texts_helper.rb` (create if doesn't exist) ```ruby module Settings module ImageToTextsHelper # Returns memory estimate for a given model def model_memory_estimate(model_name) memory_map = { "Florence-2-base" => "~460MB", "Florence-2-base-int4" => "~115MB", "Florence-2-large" => "~1.5GB", "Florence-2-large-int4" => "~385MB", "SmolVLM-256M-Instruct" => "~500MB", "SmolVLM-256M-Instruct-int8" => "~256MB", "SmolVLM-500M-Instruct" => "~1.8GB", "SmolVLM-500M-Instruct-int8" => "~500MB", "moondream2" => "~5GB", "moondream2-int8" => "~1.5GB" } memory_map[model_name] || "Unknown" end # Returns badge color class for quantized models def quantization_badge_class(model_name) if model_name.include?("int4") "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200" elsif model_name.include?("int8") "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200" else "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200" end end # Returns quantization type label def quantization_label(model_name) if model_name.include?("int4") "INT4 Optimized" elsif model_name.include?("int8") "INT8 Optimized" else "Full Precision" end end end end ``` ### Step 3.3: Update View Template **File**: `meme_search/meme_search_app/app/views/settings/image_to_texts/index.html.erb` **Enhance model card** (around line 17-64): ```erb <% @image_to_texts.each do |image_to_text| %>

<%= image_to_text.name %>

💾 <%= model_memory_estimate(image_to_text.name) %> <%= quantization_label(image_to_text.name) %> <% if image_to_text.name.include?("int4") %> ⚠️ Experimental <% end %>
<% if image_to_text.current %> Active <% end %>

<%= image_to_text.description %>

<% end %> ``` ### Step 3.4: Add Help Section **File**: `meme_search/meme_search_app/app/views/settings/image_to_texts/index.html.erb` **Add before model cards** (after line 10): ```erb

Understanding Model Options

Full Precision (FP16/BF16): Original model weights, highest quality, more memory.

INT8 Optimized: 8-bit quantization, ~50% memory reduction, minimal quality loss (0-5%). Proven for SmolVLM and Moondream.

INT4 Optimized: 4-bit quantization, ~75% memory reduction, slight quality loss (5-10%). Recommended by Microsoft for Florence models. Validate quality for your use case.

``` ### Step 3.5: Update E2E Tests **File**: `playwright/tests/image-to-texts.spec.ts` **Update model count** (currently expects 6, now 10): ```typescript test('displays the image to text settings heading', async ({ page }) => { await imageToTextsPage.goto(); expect(await imageToTextsPage.heading()).toBe('AI Models'); // Verify 10 models are displayed (was 6) const modelCards = await page.locator('.rounded-2xl').count(); expect(modelCards).toBe(10); }); test('updating the current model to all available models', async ({ page }) => { await imageToTextsPage.goto(); // Update model IDs to include new quantized variants // Assuming IDs 1-10 (Florence-2-base is 1, new models get sequential IDs) const modelIds = [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]; // Test all, end with default const allIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for (const modelId of modelIds) { await imageToTextsPage.selectModel(modelId); await imageToTextsPage.saveSelection(); expect(await imageToTextsPage.isModelSelected(modelId)).toBe(true); // Verify exclusive selection (only one active) for (const otherId of allIds) { if (otherId !== modelId) { expect(await imageToTextsPage.isModelSelected(otherId)).toBe(false); } } } }); ``` ### Step 3.6: Update README.md **File**: `README.md` **Add memory comparison table** (in Models section): ```markdown ## AI Models Meme Search supports multiple vision-language models with different memory requirements: ### Memory Comparison | Model | Parameters | Precision | Memory | Quality | Best For | |-------|-----------|-----------|--------|---------|----------| | Florence-2-base | 250M | FP16 | ~460MB | Baseline | GPU, balanced | | Florence-2-base | 250M | INT4 | **~115MB** | 90-95% | Ultra-low memory | | Florence-2-large | 700M | FP16 | ~1.5GB | High | GPU, quality | | Florence-2-large | 700M | INT4 | **~385MB** | 90-95% | Limited GPU | | SmolVLM-256M | 256M | BF16 | ~500MB | Baseline | Compact | | SmolVLM-256M | 256M | INT8 | **~256MB** | 99%+ | CPU/laptop | | SmolVLM-500M | 500M | BF16 | ~1.8GB | Enhanced | GPU/high-mem CPU | | SmolVLM-500M | 500M | INT8 | **~500MB** | 99%+ | CPU quality | | Moondream2 | 1.9B | FP16 | ~5GB | Highest | GPU 6GB+ | | Moondream2 | 1.9B | INT8 | **~1.5GB** | 95-100% | CPU/limited GPU | ### Quantization Methods - **INT8**: 8-bit integer quantization, ~50% memory reduction, minimal quality loss (0-5%) - **INT4**: 4-bit integer quantization, ~75% memory reduction, slight quality loss (5-10%) ### Hardware Recommendations **<512MB available:** - Florence-2-base-int4 (~115MB) ⭐ Recommended **512MB-1GB available:** - SmolVLM-256M-int8 (~256MB) - Florence-2-large-int4 (~385MB) **1-2GB available:** - SmolVLM-500M-int8 (~500MB) - Moondream2-int8 (~1.5GB) ⭐ Best quality in this range **2-4GB available:** - Florence-2-large (~1.5GB) - Moondream2-int8 (~1.5GB) **4GB+ available:** - Moondream2 (~5GB) ⭐ Best overall quality ``` ### Step 3.7: Update CLAUDE.md **File**: `CLAUDE.md` **Add quantization patterns section**: ```markdown ## Quantization Patterns ### Adding Quantized Model Variants Follow the established pattern from `MoondreamQuantizedImageToText`: **INT8 Pattern** (for most models): ```python from transformers import BitsAndBytesConfig quantization_config = BitsAndBytesConfig( load_in_8bit=True, llm_int8_threshold=6.0, ) model = AutoModelForCausalLM.from_pretrained( "model/name", quantization_config=quantization_config, device_map="auto", # Required - don't use .to(device) trust_remote_code=True ) ``` **INT4 Pattern** (for Florence-2 or aggressive compression): ```python import torch from transformers import BitsAndBytesConfig quantization_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4" ) model = AutoModelForCausalLM.from_pretrained( "model/name", quantization_config=quantization_config, device_map="auto", trust_remote_code=True ) ``` **Key Constraints:** - Cannot call `.to(device)` after loading with quantization - Must use `device_map="auto"` - BitsAndBytes manages device placement - No torch_dtype parameter needed - quantization config handles it - GPU-only (NVIDIA/AMD, no MPS support) ``` ## Testing Checklist ### Rails Tests - [ ] Update model count in seeds: 6 → 10 models - [ ] Run `mise exec -- bin/rails db:seed:replant` (test environment) - [ ] Verify 10 ImageToText records created - [ ] Check descriptions include hardware recommendations ### E2E Tests - [ ] Update `image-to-texts.spec.ts` for 10 models - [ ] Run `npm run test:e2e -- image-to-texts.spec.ts` - [ ] Verify all model selection tests pass - [ ] Check new badges are visible in UI ### UI Manual Testing - [ ] Navigate to Settings > AI Models - [ ] Verify 10 models displayed with badges - [ ] Check memory estimates are correct - [ ] Verify INT4 models show "Experimental" badge - [ ] Verify INT8 models show "INT8 Optimized" badge - [ ] Check help section displays correctly - [ ] Test model selection and save - [ ] Verify dark mode styling ### Documentation - [ ] README.md updated with memory table - [ ] CLAUDE.md updated with quantization patterns - [ ] Hardware recommendations are clear - [ ] Links work correctly ## Expected Outcomes ### UI Improvements - ✅ Clear memory information on each model card - ✅ Visual distinction between FP16/INT8/INT4 variants - ✅ User education section explaining quantization - ✅ Experimental warnings for INT4 models ### Documentation - ✅ Complete memory comparison table in README - ✅ Hardware recommendations for different RAM constraints - ✅ Code patterns documented in CLAUDE.md - ✅ Clear explanation of INT4 vs INT8 trade-offs ### Tests - ✅ E2E tests updated for 10 models - ✅ All tests passing - ✅ UI renders correctly in browser testing ## Completion Criteria - [ ] Rails helper methods implemented - [ ] View template enhanced with badges - [ ] Help section added to UI - [ ] E2E tests updated and passing - [ ] README.md memory table added - [ ] CLAUDE.md quantization patterns documented - [ ] Manual UI testing complete - [ ] All 10 models selectable and functional ## Next Phase After Phase 3 completion, proceed to **Phase 4: Final Validation & Deployment** (final testing, Docker builds, release preparation).