open-spell / app.py
learningDevguy's picture
Add Slang tab to Gradio UI
7ad2184 verified
"""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()