Spaces:
Sleeping
Sleeping
| """Open Spell — Gradio interface for fast, open spell correction.""" | |
| from __future__ import annotations | |
| import time | |
| import gradio as gr | |
| from spelling.corrector import SpellingCorrector | |
| _corrector = SpellingCorrector() | |
| def _timed(fn, *args, **kwargs): | |
| start = time.perf_counter() | |
| out = fn(*args, **kwargs) | |
| elapsed_ms = (time.perf_counter() - start) * 1000 | |
| return out, f"{elapsed_ms:.2f} ms" | |
| def run_spell(text: str, edit_distance: int): | |
| if not text: | |
| return "", "0.00 ms", [] | |
| corrected, t = _timed(_corrector.correct, text, max_edit_distance=int(edit_distance)) | |
| diff = [[d.original, d.corrected, d.edit_distance, f"{d.confidence:.2f}"] for d in _corrector.diff(text, corrected)] | |
| return corrected, t, diff | |
| def run_grammar(text: str, edit_distance: int): | |
| if not text: | |
| return "", "0.00 ms" | |
| corrected, t = _timed(_corrector.correct_compound, text, max_edit_distance=int(edit_distance)) | |
| return corrected, t | |
| def run_phonetic(text: str, edit_distance: int): | |
| if not text: | |
| return "", "0.00 ms" | |
| corrected, t = _timed(_corrector.correct_phonetic, text, max_edit_distance=int(edit_distance)) | |
| return corrected, t | |
| def run_domain(text: str, terms: str, edit_distance: int): | |
| if not text: | |
| return "", "0.00 ms", "0 terms loaded" | |
| added = _corrector.add_domain_terms(terms.splitlines()) if terms else 0 | |
| corrected, t = _timed(_corrector.correct, text, max_edit_distance=int(edit_distance)) | |
| return corrected, t, f"{added} term(s) added this run" | |
| def run_slang(text: str): | |
| if not text: | |
| return "", "0.00 ms" | |
| normalized, t = _timed(_corrector.normalize_slang, text) | |
| return normalized, t | |
| THEME = gr.themes.Soft(primary_hue="blue", secondary_hue="green") | |
| with gr.Blocks(title="Open Spell", theme=THEME) as demo: | |
| gr.Markdown( | |
| """ | |
| # 📝 Open Spell | |
| **Fast, open, accurate spell correction. No LLM. No GPU. No API keys.** | |
| Powered by [SymSpell](https://github.com/wolfgarbe/SymSpell) — ~1 ms per word on CPU. | |
| """ | |
| ) | |
| with gr.Tab("Spell"): | |
| gr.Markdown("Single-word typo correction. Preserves capitalization and punctuation.") | |
| with gr.Row(): | |
| spell_in = gr.Textbox(label="Input", placeholder="I lik aples", lines=3) | |
| spell_out = gr.Textbox(label="Corrected", lines=3, interactive=False) | |
| with gr.Row(): | |
| spell_edit = gr.Slider(1, 3, value=2, step=1, label="Max edit distance") | |
| spell_time = gr.Textbox(label="Latency", interactive=False, scale=1) | |
| spell_btn = gr.Button("Correct", variant="primary") | |
| spell_diff = gr.Dataframe( | |
| headers=["Original", "Corrected", "Edits", "Confidence"], | |
| label="Per-word changes", | |
| interactive=False, | |
| ) | |
| spell_btn.click(run_spell, [spell_in, spell_edit], [spell_out, spell_time, spell_diff]) | |
| gr.Examples( | |
| [["I lik aples", 2], ["The qick brwn fox", 2], ["Recieve the comittee", 2]], | |
| inputs=[spell_in, spell_edit], | |
| ) | |
| with gr.Tab("Grammar + Spell"): | |
| gr.Markdown("Handles missing spaces, extra spaces, and typos in one pass. Uses bigram model.") | |
| with gr.Row(): | |
| gram_in = gr.Textbox(label="Input", placeholder="whereis thelove", lines=3) | |
| gram_out = gr.Textbox(label="Corrected", lines=3, interactive=False) | |
| with gr.Row(): | |
| gram_edit = gr.Slider(1, 3, value=2, step=1, label="Max edit distance") | |
| gram_time = gr.Textbox(label="Latency", interactive=False, scale=1) | |
| gram_btn = gr.Button("Correct", variant="primary") | |
| gram_btn.click(run_grammar, [gram_in, gram_edit], [gram_out, gram_time]) | |
| gr.Examples( | |
| [["whereis thelove", 2], ["i amgoing tothestore", 2], ["thequickbrownfox", 2]], | |
| inputs=[gram_in, gram_edit], | |
| ) | |
| with gr.Tab("Domain"): | |
| gr.Markdown( | |
| "Add custom vocabulary (one term per line) so domain words outrank common-word neighbors. " | |
| "Useful for medical, legal, brand-name, or technical text." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| dom_in = gr.Textbox(label="Input", placeholder="pateint has hypertension", lines=3) | |
| dom_terms = gr.Textbox( | |
| label="Custom dictionary (one term per line)", | |
| placeholder="hypertension\nmyocardial\nibuprofen", | |
| lines=6, | |
| ) | |
| with gr.Column(): | |
| dom_out = gr.Textbox(label="Corrected", lines=3, interactive=False) | |
| dom_status = gr.Textbox(label="Dictionary status", interactive=False) | |
| with gr.Row(): | |
| dom_edit = gr.Slider(1, 3, value=2, step=1, label="Max edit distance") | |
| dom_time = gr.Textbox(label="Latency", interactive=False, scale=1) | |
| dom_btn = gr.Button("Correct", variant="primary") | |
| dom_btn.click(run_domain, [dom_in, dom_terms, dom_edit], [dom_out, dom_time, dom_status]) | |
| with gr.Tab("Phonetic"): | |
| gr.Markdown( | |
| "Matches by Metaphone code, so sounds-like misspellings get reranked. Great for accessibility " | |
| "and dyslexia-friendly correction." | |
| ) | |
| with gr.Row(): | |
| ph_in = gr.Textbox(label="Input", placeholder="fone numbr nite", lines=3) | |
| ph_out = gr.Textbox(label="Corrected", lines=3, interactive=False) | |
| with gr.Row(): | |
| ph_edit = gr.Slider(1, 4, value=3, step=1, label="Max edit distance") | |
| ph_time = gr.Textbox(label="Latency", interactive=False, scale=1) | |
| ph_btn = gr.Button("Correct", variant="primary") | |
| ph_btn.click(run_phonetic, [ph_in, ph_edit], [ph_out, ph_time]) | |
| gr.Examples( | |
| [["fone numbr", 3], ["nite skool", 3], ["thanx for ur halp", 3]], | |
| inputs=[ph_in, ph_edit], | |
| ) | |
| with gr.Tab("Slang"): | |
| gr.Markdown( | |
| "Expands popular SMS / internet slang into natural conversational English. " | |
| "Context-dependent slang (`bet`, `fire`, `fam`, `sus`, `lit`) is deliberately left alone." | |
| ) | |
| with gr.Row(): | |
| sl_in = gr.Textbox( | |
| label="Input", | |
| placeholder="omg idk btw fyi imo this is the best", | |
| lines=3, | |
| ) | |
| sl_out = gr.Textbox(label="Natural English", lines=3, interactive=False) | |
| sl_time = gr.Textbox(label="Latency", interactive=False) | |
| sl_btn = gr.Button("Normalize", variant="primary") | |
| sl_btn.click(run_slang, [sl_in], [sl_out, sl_time]) | |
| gr.Examples( | |
| [ | |
| ["omg im gonna lemme know asap"], | |
| ["idk btw fyi imo this is the best"], | |
| ["tldr the meeting is at 3pm tbh"], | |
| ["wyd rn wanna grab coffee"], | |
| ["finna head out brb"], | |
| ["gotta finish this asap ngl"], | |
| ], | |
| inputs=[sl_in], | |
| ) | |
| gr.Markdown( | |
| """ | |
| --- | |
| **Open source · MIT licensed · No telemetry** · [GitHub-equivalent: see Files tab](.) | |
| """ | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |