""" Dyslexic Engine — Text should work for YOUR brain. Built for the Build Small Hackathon 2026. Shows the original text, then three dyslexic-friendly versions beneath: 1. Spaced Syllables — dashes between syllables 2. Sound It Out — simple phonemes with spaces 3. Slash Flow — phonemes with slashes Runs entirely local. No cloud APIs. No data leaves your machine. """ import gradio as gr import pyphen import os import re from openai import OpenAI from phonemes import text_to_ipa # --- NVIDIA Nemotron --- NVIDIA_API_KEY = os.environ.get("NVIDIA_API_KEY", "") NEMOTRON_MODEL = "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning" nvidia_client = None if NVIDIA_API_KEY: nvidia_client = OpenAI( base_url="https://integrate.api.nvidia.com/v1", api_key=NVIDIA_API_KEY, ) print("NVIDIA Nemotron connected.") else: print("No NVIDIA_API_KEY — running in fallback mode.") # --- Syllable Engine --- dic = pyphen.Pyphen(lang="en_US") def get_word_parts(word): """Extract prefix punctuation, core word, suffix punctuation.""" prefix = "" suffix = "" core = word while core and not core[0].isalnum(): prefix += core[0] core = core[1:] while core and not core[-1].isalnum(): suffix = core[-1] + suffix core = core[:-1] return prefix, core, suffix def split_syllables(word): """Split a word into syllables using pyphen.""" clean = re.sub(r'[^\w]', '', word) if not clean: return [word] hyphenated = dic.inserted(clean) parts = hyphenated.split("-") return parts if parts else [word] # --- VERSION 1: Spaced Syllables --- def spaced_syllables(text): """Each word broken into syllables with ' - ' between them.""" words = text.split() result = [] for word in words: prefix, core, suffix = get_word_parts(word) if not core: result.append(word) continue syls = split_syllables(core) if len(syls) > 1: spaced = " - ".join(syls) else: spaced = syls[0] result.append(f"{prefix}{spaced}{suffix}") return " ".join(result) # --- VERSION 2 & 3: Phoneme versions (LLM-generated) --- def generate_phonemes(text): """Use the LLM to generate simple phonetic pronunciations for every word. Returns two versions: spaced and slashed.""" if nvidia_client: prompt = f"""Convert every word in this paragraph to simple phonetic pronunciation. Rules: - Use simple everyday letter combinations, NOT IPA symbols - Capitalize the stressed syllable in multi-syllable words - Keep the same word order as the original - Separate sounds within a word with spaces - Short words (a, the, is, by) still get converted - Output ONLY the phonetic version, nothing else Example: Input: "The cat sat on the mat" Output: "thuh kat sat on thuh mat" Input: "Photosynthesis is the process" Output: "foh toh SIN thuh sis iz thuh PRAH sess" Now convert this text: {text[:600]}""" try: response = nvidia_client.chat.completions.create( model=NEMOTRON_MODEL, messages=[ {"role": "system", "content": "You convert text to simple phonetic pronunciation. Output ONLY the pronunciation, keeping the same word order. Use everyday letter combinations, not IPA. Capitalize stressed syllables. Separate sounds within words with spaces."}, {"role": "user", "content": prompt} ], max_tokens=800, temperature=0.2, ) spaced_phonemes = response.choices[0].message.content.strip() slash_phonemes = convert_spaced_to_slashed(spaced_phonemes) return spaced_phonemes, slash_phonemes except Exception as e: fallback = _rule_based_phonemes(text) return fallback, fallback.replace(" ", " ").replace(" ", "/").replace("//" , " ") else: fallback = _rule_based_phonemes(text) return fallback, fallback def convert_spaced_to_slashed(spaced_text): """Convert spaced phoneme text to slash-separated version. Words are separated by double spaces, sounds within words by single spaces.""" # Split on double-space (word boundaries) word_chunks = re.split(r' +', spaced_text) slashed = [] for chunk in word_chunks: chunk = chunk.strip() if not chunk: continue # If the chunk has spaces, those are sound boundaries within a word sounds = chunk.split() if len(sounds) > 1: slashed.append("/".join(sounds)) else: slashed.append(chunk) return " ".join(slashed) def _rule_based_phonemes(text): """Fallback: just use syllable splitting as a rough phoneme guide.""" words = text.split() result = [] for word in words: prefix, core, suffix = get_word_parts(word) if not core: result.append(word) continue syls = split_syllables(core) result.append(f"{prefix}{' '.join(syls).lower()}{suffix}") return " ".join(result) # --- VERSION 4: IPA "Professional" Phonetic (the punchline) --- def generate_ipa(text): """Generate full IPA transcription from the 44 phonemes mapping. Deliberately unreadable. That's the entire point. Uses our phoneme engine — no LLM needed, instant, and accurate enough to look terrifyingly professional.""" return text_to_ipa(text) # --- Main Transform --- def transform_text(text): """Generate all three versions from input text.""" if not text or not text.strip(): return "
Paste some text above to get started.
" # Version 1: Spaced syllables (rule-based, instant) v1 = spaced_syllables(text) # Version 2 & 3: Phoneme versions (LLM) v2_spaced, v3_slashed = generate_phonemes(text) # Version 4: IPA — the "professional" version nobody can read v4_ipa = generate_ipa(text) html = f"""Text should work for your brain. Not the other way around.