| # BUILD PROMPT β paste everything below this line into Codex / OpenCode | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| You are a senior Python + Gradio engineer. Build a Hugging Face Space called **"Third Eye"**: a | |
| fully voice-driven accessibility app for blind / low-vision users. The user points a webcam at | |
| something (menu, medicine label, sign, scene), speaks a question, and hears the answer back in | |
| their language. **Zero typing required** on the happy path. | |
| Work in the current project folder. Build **incrementally** and **verify at every checkpoint**. | |
| Do not write the whole app at once. Do not invent APIs. Follow these rules exactly. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ## ABSOLUTE RULES (do not break these) | |
| 1. **Verify before coding inference.** Model load/call APIs differ by version. For every model, | |
| open its Hugging Face model card and confirm the exact model ID, load call, and inference call | |
| BEFORE writing that stage. If a tool to fetch docs is available (e.g. context7), use it. If a | |
| model ID does not resolve, **STOP and report it** β never silently substitute another model. | |
| 2. **Sponsor models ONLY** (table below). No OpenAI/Whisper/Google/etc. anywhere. | |
| 3. **Build in phases. Stop at each CHECKPOINT and confirm it passes before continuing.** | |
| 4. **Never show a raw traceback to the user.** Every stage is wrapped; failures become | |
| `gr.Warning("friendly message")` and a graceful fallback. | |
| 5. **MUST-HAVE before SHOULD-HAVE before NICE-TO-HAVE.** A working minimal app beats a broken | |
| complete one. If you run low on time, ship the vertical slice. | |
| 6. When unsure about an API after reading the card, write the smallest possible test script and run | |
| it before wiring that stage into the UI. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ## MODELS (only these β verify each card before use) | |
| | Role | Model ID | Params | Sponsor | | |
| |---|---|---|---| | |
| | Vision + OCR (PRIMARY) | `openbmb/MiniCPM-V-2_0` | 2.8B | OpenBMB | | |
| | Vision + OCR (FALLBACK only if quality unacceptable) | `openbmb/MiniCPM-V-4_5` | 8B | OpenBMB | | |
| | Speech-to-text | `CohereLabs/cohere-transcribe-03-2026` | 2B | Cohere | | |
| | Text-to-speech | `openbmb/VoxCPM2` | 2B | OpenBMB | | |
| Primary param budget = 2.8B (β€ 4B β qualifies for Tiny Titan). If you must use the 8B fallback, | |
| write in the README: "fallback used, Tiny Titan badge forfeited." Never swap silently. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ## TECH STACK | |
| - Gradio 5.x (`gr.Blocks`) on a Hugging Face Space (`sdk: gradio`). | |
| - Modal serverless GPU (A10G) runs vision + TTS + STT. | |
| - Python 3.11. No other cloud APIs. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ## TARGET FILE TREE | |
| ``` | |
| app.py # Gradio UI + orchestration | |
| modal_backend.py # Modal app: describe_scene(), speak(), transcribe_audio() | |
| cohere_stt.py # Cohere Transcribe wrapper (imported by modal_backend) | |
| utils.py # image<->bytes, bytes<->wav, safe_call wrapper | |
| requirements.txt | |
| .env.example # MODAL_TOKEN_ID, MODAL_TOKEN_SECRET, HF_TOKEN | |
| README.md # HF frontmatter + story + edge section | |
| BLOG.md # Field Notes draft | |
| DEMO_SCRIPT.md # 45s shot list | |
| assets/ | |
| custom.css # "Iris" design system | |
| sample_menu.jpg sample_label.jpg sample_sign.jpg (use 3 royalty-free / your own photos) | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 0 β VERIFY REALITY (do this first, write NO inference code yet) | |
| For each model (`MiniCPM-V-2_0`, `VoxCPM2`, `cohere-transcribe-03-2026`): | |
| - Confirm the model ID resolves on Hugging Face. | |
| - Read the card's usage example. Record the EXACT: import, `from_pretrained` args | |
| (`trust_remote_code`, dtype, etc.), and the inference call signature. | |
| - Note especially: MiniCPM-V's `model.chat(...)` signature varies β some versions take | |
| `image=<PIL>, msgs=[{"role":"user","content":<str>}]`; others take | |
| `image=None, msgs=[{"role":"user","content":[<PIL>, <str>]}]`. Use whatever THIS card shows. | |
| - For VoxCPM2: find the real synthesis call (it may need a reference voice / a `generate` method, | |
| not `model.synthesize`). For Cohere Transcribe: confirm whether it loads via `transformers` | |
| `pipeline("automatic-speech-recognition", ...)` or needs a custom call. | |
| **CHECKPOINT 0:** Output a short table of the verified API for each model (load call + infer call). | |
| If anything can't be verified, list it explicitly and propose the smallest fix. Then continue. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 1 β SCAFFOLD (UI renders with fake data) | |
| Create all files. Use STUB functions that return canned data so the UI runs with no GPU. | |
| `requirements.txt`: | |
| ``` | |
| gradio>=5.0 | |
| modal | |
| pillow | |
| soundfile | |
| numpy | |
| ``` | |
| (Add `transformers`, `torch`, `accelerate`, `sentencepiece`, `timm` to the **Modal image**, not the | |
| Space requirements β the Space does not run the models locally.) | |
| `utils.py` β implement: | |
| ```python | |
| import io, base64, tempfile, numpy as np | |
| from PIL import Image | |
| def image_to_bytes(image) -> bytes: | |
| if isinstance(image, np.ndarray): | |
| image = Image.fromarray(image) | |
| buf = io.BytesIO(); image.convert("RGB").save(buf, format="JPEG"); return buf.getvalue() | |
| def bytes_to_wav(audio_bytes: bytes) -> str: | |
| f = tempfile.NamedTemporaryFile(delete=False, suffix=".wav") | |
| f.write(audio_bytes); f.close(); return f.name | |
| def safe_call(fn, *args, fallback=None, warn="Something went wrong.", **kwargs): | |
| import gradio as gr | |
| try: | |
| return fn(*args, **kwargs) | |
| except Exception as e: | |
| gr.Warning(f"{warn} ({type(e).__name__})") | |
| return fallback | |
| ``` | |
| `app.py` β build `gr.Blocks(css=open("assets/custom.css").read())` with: | |
| - A header with the **Iris orb** (a `gr.HTML` div, class `iris idle`) + an ARIA live status line. | |
| - A language `gr.Dropdown` (English, Hindi, German, Tamil, Telugu, Kannada), default English. | |
| - Three `gr.Tab`s: **Describe**, **Ask**, **Read Text**. Each has a `gr.Image(sources=["webcam","upload"])`, | |
| Ask also has `gr.Audio(sources=["microphone"])`, plus a large primary button, a `gr.Audio` output | |
| (set `autoplay=True`), and a large-font `gr.Textbox` output for the transcript. | |
| - The 3 sample images wired as `gr.Examples` so judges can test with no webcam. | |
| - For now, button click calls a STUB `run_pipeline(...)` that returns a placeholder wav path + text. | |
| **CHECKPOINT 1:** `python app.py` launches locally; UI loads; clicking a button shows placeholder | |
| text and the Iris orb is visible. No GPU involved yet. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 2 β MODAL VISION (real description) | |
| `modal_backend.py`: create the Modal app + GPU image, implement `describe_scene` using the | |
| **verified** MiniCPM-V API from Phase 0. | |
| ```python | |
| import modal | |
| app = modal.App("third-eye-backend") | |
| vision_image = modal.Image.debian_slim().pip_install( | |
| "transformers>=4.40","torch","pillow","accelerate","sentencepiece","timm","soundfile") | |
| @app.function(gpu="A10G", image=vision_image, timeout=180) | |
| def describe_scene(image_bytes: bytes, question: str, lang: str = "en") -> str: | |
| import io, torch | |
| from PIL import Image | |
| from transformers import AutoModel, AutoTokenizer | |
| model_id = "openbmb/MiniCPM-V-2_0" | |
| model = AutoModel.from_pretrained(model_id, trust_remote_code=True, | |
| torch_dtype=torch.float16).cuda().eval() | |
| tok = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True) | |
| image = Image.open(io.BytesIO(image_bytes)).convert("RGB") | |
| prompt = question.strip() or "Describe everything you see in detail." | |
| # >>> USE THE EXACT .chat() SIGNATURE YOU VERIFIED IN PHASE 0 <<< | |
| return model.chat(image=image, msgs=[{"role":"user","content":prompt}], tokenizer=tok) | |
| ``` | |
| Deploy: `modal deploy modal_backend.py`. Then write `test_vision.py` that reads `sample_menu.jpg`, | |
| calls `describe_scene.remote(...)`, prints the answer. | |
| **CHECKPOINT 2:** Real, sensible text comes back for the menu image. If quality is unusable, switch | |
| to `MiniCPM-V-4_5` and note the forfeit in README (do not do this lightly). | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 3 β MODAL TTS (real speech) | |
| Add `speak(text, lang)` to `modal_backend.py` using the **verified** VoxCPM2 API. Return WAV bytes. | |
| Write `test_tts.py` that synthesizes "Hello, this is Third Eye." and saves `out.wav`. | |
| **CHECKPOINT 3:** `out.wav` plays intelligible speech. (If multilingual needs a lang/voice arg, wire | |
| `lang` through now.) | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 4 β WIRE "DESCRIBE" END-TO-END β MINIMUM VALID SUBMISSION | |
| Replace the stub `run_pipeline` in `app.py`: | |
| ```python | |
| def run_pipeline(image, audio_path, mode, lang): | |
| if image is None: | |
| gr.Warning("No image captured. Point the camera and try again.") | |
| return None, "No image captured.", "" | |
| img_bytes = image_to_bytes(image) | |
| if mode == "Ask" and audio_path: | |
| question = safe_call(transcribe_audio.remote, audio_path, | |
| warn="Couldn't hear you β type your question instead.", fallback="") | |
| elif mode == "Read Text": | |
| question = "Read all text visible in this image, word by word, exactly as written." | |
| else: | |
| question = "Describe everything in this image in detail for a blind user." | |
| answer = safe_call(describe_scene.remote, img_bytes, question, lang, | |
| warn="Vision model is waking up β try once more.", fallback="") | |
| if not answer: | |
| return None, "Could not analyze the image.", question | |
| audio_bytes = safe_call(speak.remote, answer, lang, warn="Voice unavailable β showing text.", | |
| fallback=None) | |
| audio_out = bytes_to_wav(audio_bytes) if audio_bytes else None | |
| return audio_out, answer, question | |
| ``` | |
| Show `gr.Progress` with "Loading AI models (first run: ~30s)β¦" around the first heavy call. | |
| **CHECKPOINT 4:** In the running Space/app, pick `sample_menu.jpg` in the Describe tab β audio | |
| auto-plays a description + the transcript shows. THIS IS THE MINIMUM VALID SUBMISSION. Commit here. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 5 β STT + "ASK" (zero-typing loop) | |
| Implement `transcribe_audio(audio_path)` in `modal_backend.py` (delegates to `cohere_stt.py`) using | |
| the verified Cohere Transcribe API. Wire the Ask tab: mic β transcribe β describe β speak. | |
| **CHECKPOINT 5:** Record a spoken question about the sample image β hear a spoken answer. No typing. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 6 β "READ TEXT" + LANGUAGE + CSS POLISH | |
| - Read Text tab uses the fixed OCR prompt (already in run_pipeline). | |
| - Confirm the language dropdown changes TTS output language (test English + Hindi minimum). | |
| - Build the real **Iris** `assets/custom.css` (see DESIGN SPEC below). Drive orb state from `app.py` | |
| by updating the orb HTML's class (idle / listening / seeing / thinking / speaking) at each stage. | |
| **CHECKPOINT 6:** All three tabs work; Hindi TTS works; UI matches the Iris spec. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 7 β HARDENING | |
| - Cold-start progress shown on first call. | |
| - Mic failure β reveal a `gr.Textbox` typed-question fallback (never block). | |
| - TTS failure β large-font text output only, with a `gr.Warning`. | |
| - Every stage wrapped in `safe_call`; no traceback ever reaches the user. | |
| - Confirm 3 examples load and run with no webcam. | |
| **CHECKPOINT 7:** Manually break the mic and break TTS β the app degrades gracefully, never crashes. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 8 β SUBMISSION ASSETS | |
| `README.md` β start with this frontmatter VERBATIM: | |
| ```yaml | |
| --- | |
| title: Third Eye | |
| emoji: ποΈ | |
| colorFrom: indigo | |
| colorTo: blue | |
| sdk: gradio | |
| sdk_version: "5.0" | |
| app_file: app.py | |
| pinned: false | |
| tags: | |
| - hackathon | |
| - build-small | |
| - backyard-ai | |
| - accessibility | |
| - blind | |
| - openbmb/MiniCPM-V-2_0 | |
| - openbmb/VoxCPM2 | |
| - cohere/cohere-transcribe-03-2026 | |
| - tiny-titan | |
| - off-brand | |
| --- | |
| ``` | |
| Then write: what it is, who it's for, how to use, models+sizes table (call out 2.8B Tiny Titan), | |
| architecture paragraph, **on-device/edge section** (honest claim + roadmap: these models quantize to | |
| int4 GGUF and can run offline on a phone via llama.cpp β framed as roadmap, not a shipped phone | |
| build), accessibility & Iris design, run-it-yourself (env vars + `modal deploy` + Space secrets: | |
| MODAL_TOKEN_ID / MODAL_TOKEN_SECRET / HF_TOKEN), credits (OpenBMB, Cohere, Modal, HF). | |
| `BLOG.md` β a Field Notes draft: "What VLM quality really feels like at 2.8B" (what worked, where | |
| MiniCPM-V-2 struggled vs the 8B fallback, OCR accuracy notes). `DEMO_SCRIPT.md` β the 45β60s shot | |
| list (eye opens β blindfolded menu read aloud in Hindi β label/sign cuts β tagline). | |
| **CHECKPOINT 8:** Space builds clean, loads on a cold visit, all assets present. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PHASE 9 β NICE-TO-HAVE (only if time remains, in this order) | |
| 1. Bounding-box "Zoom & Read" tab via `gr.ImageEditor`: user draws a rectangle, the crop is sent to | |
| MiniCPM-V with "Read the text in this image exactly as written." | |
| 2. Cache model weights on a `modal.Volume` to cut cold-start time. | |
| 3. A small GGUF int4 on-device proof + a benchmark table in README (params, int4 size, target device). | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # DESIGN SPEC β "Iris" custom CSS (Off-Brand track) | |
| Accessibility constraints rendered as a futuristic aesthetic. Pure CSS so it's reliable to build. | |
| - **Background** `#06070A` + faint radial vignette; optional subtle grain. | |
| - **Accent gradient** `#5B7CFA β #3DE0FF`; glows via layered `box-shadow`. | |
| - **Text** `#F5F7FA`; base font 20px, output text 24px+, line-height 1.7; contrast β₯ WCAG AA. | |
| - **Iris orb**: a centered circular `div` (~140px) with the accent radial gradient and a soft outer | |
| glow. Define keyframe animations per state class: | |
| - `.iris.idle` slow breathing scale 1.0β1.04 (~4s). | |
| - `.iris.listening` pulsing ring. | |
| - `.iris.seeing` a scan-line sweep. | |
| - `.iris.thinking` faster, tighter pulse. | |
| - `.iris.speaking` waveform-like glow pulse synced loosely to playback. | |
| - **Primary button**: large pill / circle, min 96px hit target, accent gradient, thick cyan focus ring. | |
| - **Surfaces**: glass panels β `backdrop-filter: blur(12px)`, 1px hairline border, subtle inner glow. | |
| - **Motion**: wrap ALL animations in `@media (prefers-reduced-motion: reduce)` to disable them. | |
| - **Focus**: visible thick cyan focus ring on every interactive element (serves keyboard + the look). | |
| - The ARIA `live=polite` status line mirrors the orb state in words for screen-reader users. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # DONE = ALL OF THIS TRUE | |
| - [ ] Describe / Ask / Read Text all work end-to-end, zero typing on the happy path. | |
| - [ ] Audio answers auto-play; transcript shown large. | |
| - [ ] Language dropdown drives multilingual TTS (English + Hindi verified). | |
| - [ ] 3 bundled examples run with no webcam. | |
| - [ ] Cold-start progress + mic/TTS fallbacks + `gr.Warning` everywhere; no raw tracebacks. | |
| - [ ] Iris custom CSS live (Off-Brand); WCAG-AA contrast. | |
| - [ ] README frontmatter verbatim; edge/on-device section honest; BLOG.md + DEMO_SCRIPT.md present. | |
| - [ ] Primary model 2.8B β€ 4B (Tiny Titan) β stated in README. | |
| - [ ] Space builds clean and loads cold. | |
| Report which checkpoints passed and paste the final file tree when done. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |